knitr::opts_chunk$set(echo = TRUE, warning=FALSE, message=FALSE, cache = TRUE)
library(Matrix)
library(DropletUtils)
library(SoupX)
library(ggplot2)
library(cowplot)
library(tidyverse)
library(magrittr)
library(Seurat)
library(DoubletFinder)
library(reticulate)
library(scPred)
library(SeuratDisk)
library(speckle)
library(RColorBrewer)
library(limma)
library(MAST)
library(clusterProfiler)
library(org.Mm.eg.db)

setwd("~/Documents/Begun/tms_3_and_18m_droplet_kidney/")

read raw matrices and select cells from empty drops

read.sc <- function(x) { 
  mydata <- read10xCounts(paste0(x, "/"))  # read cellranger output
  mydata
}

samples <- c("3-M-8",     # sample names
             "3-M-9",
             "3-F-57",
             "18-F-50",
             "18-F-51",
             "18-M-52")

sce.list <- sapply(samples, read.sc) # import all six samples and store in a list of Single Cell Experiments

calculate barcode ranks and visualize with waterfall plots:

br <- lapply(sce.list, barcodeRanks) # function that ranks barcodes by #UMIs
sapply(br, function(i) {i@metadata}) # show knee and inflection point
           mo3_M_8 mo3_M_9 mo3_F_57 mo18_F_50 mo18_F_51 mo18_M_52
knee       6252    2512    9150     14431     9991      10938    
inflection 461     906     785      657       1164      1407     
waterfall_plot <- function(s) {
  br.df <- as.data.frame(s)   
  br.df %<>% distinct(total, .keep_all = TRUE) # remove redundant datapoints to reduce size that has to go to memory when plotting
  br.points = data.frame(
    point = c("knee", "inflection"),
    val = c(s@metadata$inflection, s@metadata$knee))
  ggplot(data = br.df, aes(x = rank, y = (total + 1))) +
    geom_point(size = 0.75) +
    geom_hline(data = br.points,
               aes(yintercept = val, colour = point), linetype = 2) +
    scale_x_log10() +
    scale_y_log10() +
    labs(x = "log(barcode rank)", y = "log(UMI counts)") +
    theme_minimal(base_size = 7)
}

br.waterfall_plots <- lapply(br, waterfall_plot)

plot_grid(plotlist = br.waterfall_plots, 
          labels = samples, 
          label_size = 7) # plot all six samples

use the EmptyDrops algorithm to predict putative cells:

set.seed(8)
ed.out <- sapply(sce.list, function(i) { emptyDrops(i, lower = 200) }) # requiring at least 200 UMI

# filter to cells with FDR of 0.01
cells <- lapply(ed.out, function(i) { which(i$FDR <= 0.01) } )

#subset SCEs with cell indices
sce.filtered.list <- mapply(function(x, y) { x[,y] },
                            sce.list, cells)
# take a look at waterfall plots for the cells predicted by EmptyDrops
br.filtered <- lapply(sce.filtered.list, barcodeRanks)
br.filtered.waterfall_plots <- lapply(br.filtered, waterfall_plot)
plot_grid(plotlist = br.filtered.waterfall_plots, 
          labels = samples, 
          label_size = 7) # plot all six samples

EmptyDrops predicts that many of the barcodes below the knee are real cells based on their transcriptome. This can be useful when trying to mazimize the number of cells captured, but many of these might be low quality cells / fractions of cells / nuclei. We can filter these out later with a hard UMI cutoff around the knee point of ~1000 UMI.

## export filtered data
dir.create("filtered")
lapply(names(sce.filtered.list), # create directory for each filtered sample
       function(i) {
       dir.create(file.path("filtered", i)) } )

# now write in 10X format
mapply(function(x, y) {
         writeMM(counts(x), file = file.path("filtered", y, "matrix.mtx"))  # sparse matrix
         write_lines(x$Barcode, file = file.path("filtered", y, "barcodes.tsv")) # barcode file
         write.table(as.data.frame(rowData(x)), # genes file
                     file = file.path("filtered", y, "genes.tsv"), 
                     sep = "\t", col.names = FALSE, row.names = FALSE, quote = FALSE)
       },
       sce.filtered.list,
       names(sce.filtered.list)
)

# remove these data now 
rm(sce.list, sce.filtered.list, ed.out, br, br.filtered)

read filtered matrices and perform QC

prior to batch-correction and integration I do QC on each sample seperately.

reading in the sparse matrices of filtered cells:

read.sc <- function(x) {
  mydata <- readMM(paste0(x, "/matrix.mtx"))
  features <- read.table(paste0(x, "/genes.tsv"), header = FALSE)
  barcodes <- read.table(paste0(x, "/barcodes.tsv"), header = FALSE)
  dimnames(mydata) <- list(features$V2, barcodes$V1) # rownames are gene symbols and colnames are barcodes
  mydata
}

setwd("~/Documents/Begun/tms_3_and_18m_droplet_kidney/filtered/") # filtered cells dir

samples <- c("mo3_M_8", # sample names are updated to strings that work with R
             "mo3_M_9",
             "mo3_F_57",
             "mo18_F_50",
             "mo18_F_51",
             "mo18_M_52")

filt.mats <- lapply(samples, read.sc) # reading in filtered matrices to a list
names(filt.mats) <- samples

initiate seurat objects from each of six gene X cell matrices:

so.list <- lapply(filt.mats, function(i) {
                    CreateSeuratObject(counts = i, 
                                       min.cells = 10, # keep only genes expressed in at least 10 cells
                                       project = "kidney")
              }
        )
Warning: Non-unique features (rownames) present in the input matrix, making unique
Warning: Non-unique features (rownames) present in the input matrix, making unique
Warning: Non-unique features (rownames) present in the input matrix, making unique
Warning: Non-unique features (rownames) present in the input matrix, making unique
Warning: Non-unique features (rownames) present in the input matrix, making unique
Warning: Non-unique features (rownames) present in the input matrix, making unique
rm(filt.mats) # don't need the matrices anymore

QC based on mitochondrial gene %, number of UMIs, number of features.

# mtDNA
so.list <- lapply(so.list, function(i) {
              i$percent.mt <- PercentageFeatureSet(i, pattern = "^mt-")
              i
            }
          )
## look at dist of mtDNA, nUMI, nFeatures
QC.vln.plots <- lapply(so.list, function(data) { 
                        VlnPlot(data,
                        features = c("nFeature_RNA", "nCount_RNA", "percent.mt")) & 
                        theme(axis.title.x = element_blank(), axis.text.x = element_blank())
                      }
                    )
plot_grid(plotlist = QC.vln.plots, ncol = 1)

#QC.vln.plots[1]
#QC.vln.plots[2]
#QC.vln.plots[3]
#QC.vln.plots[4]
#QC.vln.plots[5]
#QC.vln.plots[6]
# plot % mitochondrial counts vs total counts per cell
QC.mitoscatter.plots <- lapply(so.list, function(data) { 
                                FeatureScatter(data,
                                feature2 = "percent.mt", 
                                feature1 = "nCount_RNA",
                                pt.size = 0.5, cols = "black",) & 
                                theme_minimal(base_size = 8) & 
                                theme(legend.position = "none") &
                                labs(title = NULL)
                              }
                            )

plot_grid(plotlist = QC.mitoscatter.plots, labels = samples, label_size = 10)

see the expected trend of high mito% cells also having lower counts, but this is not strictly true. Next step is to apply a reasonable cutoff, based on these distributions but also a priori knowledge. typically we would choose ~10% mitochondrial counts. However, this data does have a high percentage of mitochondrial counts so that would remove many cells. # apply some reasonable upper cutoffs based on these distributions and plot again. Also it seems the kidney expresses a lot of mt genes and exclusion criteria is somewhat contested: 30 - 50%: https://www.ncbi.nlm.nih.gov/pmc/articles/PMC6940381/

based on this 40% seems reasonable:

so.list.mitofilter <- lapply(so.list, function(i) {
                      subset(i, percent.mt < 40)  # subset to cells with < 40% mito counts
                }
              )
# what fraction of cells did we remove?
mapply(function(before, after) { 
        (dim(before)[2] - dim(after)[2]) / dim(before)[2] 
      },
      so.list, so.list.mitofilter
    )
   mo3_M_8    mo3_M_9   mo3_F_57  mo18_F_50  mo18_F_51  mo18_M_52 
0.05957109 0.15534554 0.11095170 0.06547961 0.06336725 0.05086849 

filter by UMIs and features note that emptydrops finds a lot of low UMI barcodes it thinks are cells, but they could also be are nuclei / cellular debris. for a conservative analysis using high quality cells with a hard cutoff may be more prudent

so.list.cutoff <- lapply(so.list.mitofilter, function(i) {
                          subset(i, nCount_RNA < 30000 & nCount_RNA > 1200)
                        }
                      )

# what fraction of cells did we remove?
mapply(function(before, after) { 
          (dim(before)[2] - dim(after)[2]) / dim(before)[2] 
          },
        so.list.mitofilter, so.list.cutoff
      )
  mo3_M_8   mo3_M_9  mo3_F_57 mo18_F_50 mo18_F_51 mo18_M_52 
0.4332770 0.3720280 0.2781065 0.4019668 0.3274074 0.5901961 

plot QC data again:

QC.vln.plots <- lapply(so.list.cutoff, function(data) { 
                        VlnPlot(data,
                        features = c("nFeature_RNA", "nCount_RNA", "percent.mt")) & 
                        theme(axis.title.x = element_blank(), axis.text.x = element_blank())
                      }
                    )
plot_grid(plotlist = QC.vln.plots, ncol = 1)

remove unfiltered data now

rm(so.list, so.list.mitofilter)

data normalization

normalize the data using the SCTransform method. this uses a regularized negative binomial regression to estimate regularized parameters for different groups of genes. method outperforms using a single scale factor for all genes: https://genomebiology.biomedcentral.com/articles/10.1186/s13059-019-1874-1

so.list.cutoff <- lapply(so.list.cutoff, function(i) { 
                                          SCTransform(i, 
                                          vars.to.regress = "percent.mt", # regressing normalization against mitochondrial count percent
                                          method = "glmGamPoi",
                                          verbose = FALSE)
                                        }
                                      )

preliminary dimensionality reduction

doing dim reduction and clustering on each sample before batch correction. THis is to make sure the data looks ok and this will help visualize doublets later

# run PCA and decide how many PCs to use for clustering and UMAP
#so.list.cutoff <- lapply(so.list.cutoff,
 #                        function(i) { i %>% RunPCA() })

elbowplots <- lapply(so.list.cutoff, function(i) { 
                          ElbowPlot(i, ndims = 40, reduction = "pca") + 
                          theme_minimal(base_size = 8)
                        }
                      )
plot_grid(plotlist = elbowplots, labels = samples, label_size = 8)

explained variance looks very small after PC 26

PCs <- 1:26

continue with SNN graph construction, UMAP, Leiden clustering

so.list.cutoff <- lapply(so.list.cutoff,
                         function(i) {
                           i %>% 
                             FindNeighbors(dims = PCs) %>%  # make SNN graph
                             RunUMAP(dims = PCs) %>%        # perform UMAP
                             FindClusters(algorithm = "leiden") # do clustering with Leiden algorithm
                         })

check the most variable features among samples for general overlap:

top20 <- lapply(so.list.cutoff, function(i) { head(VariableFeatures(i), 20) }) # top 20 variable features
knitr::kable(as.data.frame(do.call(cbind, top20)), format = "markdown")
mo3_M_8 mo3_M_9 mo3_F_57 mo18_F_50 mo18_F_51 mo18_M_52
Kap Kap Umod Tmsb4x Tmsb4x Umod
Umod Umod Slc12a1 Umod Cd74 Cd74
Slc12a1 Gpx3 Egf Slc12a1 Umod H2-Aa
Egf Cd74 Tmsb4x Egf H2-Aa H2-Eb1
Tmsb4x H2-Aa Cd74 Cd74 H2-Eb1 H2-Ab1
Plvap H2-Ab1 Slc12a3 Slc12a3 H2-Ab1 Fabp4
Ly6c1 H2-Eb1 Wfdc15b H2-Aa Slc12a1 Slc12a1
Cd74 Egf H2-Aa H2-Ab1 Egf Egf
Wfdc15b Slc12a1 H2-Ab1 H2-Eb1 Slc12a3 C1qb
Fabp4 Slc12a3 H2-Eb1 Wfdc15b C1qb C1qa
S100g Fabp4 Klk1 Defb1 C1qa Slc12a3
H2-Aa Tmsb4x C1qb Klk1 Lyz2 C1qc
Slc12a3 S100g C1qa C1qb C1qc Klk1
H2-Eb1 C1qb C1qc Fcer1g Wfdc15b Lyz2
Klk1 C1qa Emcn C1qa Napsa Gpx3
H2-Ab1 Plvap Fcer1g Cd52 Fcer1g Tmsb4x
Emcn C1qc Klf2 Ctss Ctss Kap
Plpp1 Igfbp3 Tm4sf1 Calb1 Klk1 Plvap
Defb1 Klk1 Fabp4 Tmsb10 Defb1 Ly6c1
Klf2 Ctss Plvap S100g Tyrobp Esm1

plot variance against average gene expression:

VF.plots <- lapply(so.list.cutoff, function(i) {
                                         VariableFeaturePlot(i, pt.size = 0.5, 
                                                             selection.method = "sct") +
                                         theme_minimal(base_size = 8)
                                       }
                                    )

options(ggrepel.max.overlaps = Inf)

VF.plots.labs <- mapply(function(vf, labels) {
                        LabelPoints(plot = vf, 
                                    points = labels, 
                                    repel = TRUE,
                                    xnudge = 0,
                                    ynudge = 0)
                      },
                   VF.plots, top20, SIMPLIFY = FALSE
                  )

plot_grid(plotlist = VF.plots.labs, ncol = 1, labels = samples)

preliminary dim reduction / clustering visualization

this is to make sure the data doesn’t look out of the ordinary on per-sample basis. we will use these clusters to do doublet detection.

# this function adjusts alpha (transparency) of points in ggplot objects
adjust.alpha <- function(plot, a) { 
                    plot[[1]]$layers[[1]]$aes_params$alpha <- a 
                    plot
                   }
# PC1 vs PC2
PCA.12.plots <- lapply(so.list.cutoff, function(i) {
                       DimPlot(object = i, dims = c(1,2), reduction = "pca") +
                       theme_minimal(base_size = 8)
                      }
                    )

PCA.12.plots <- lapply(PCA.12.plots, adjust.alpha, a = 0.7)
plot_grid(plotlist = PCA.12.plots, labels = samples)

# PC1 vs PC3
PCA.13.plots <- lapply(so.list.cutoff, function(i) {
                       DimPlot(object = i, dims = c(1,3), reduction = "pca") +
                       theme_minimal(base_size = 8)
                      }
                    )

PCA.13.plots <- lapply(PCA.13.plots, adjust.alpha, a = 0.7)
plot_grid(plotlist = PCA.13.plots, labels = samples)

# UMAP
umap.plots <- lapply(so.list.cutoff, function(i) {
                     DimPlot(object = i, reduction = "umap", pt.size = 0.75) +
                     theme_minimal(base_size = 8)
                      }
                    )

umap.plots <- lapply(umap.plots, adjust.alpha, a = 0.7)
plot_grid(plotlist = umap.plots, labels = samples)

Sanity check with some marker genes from Tabula Muris

Acta2 (mesangial cells):

Acta2.plots <- lapply(so.list.cutoff, function(i) {
                     FeaturePlot(object = i, features = "Acta2", reduction = "umap") +
                     theme_minimal(base_size = 8) +
                     theme(title = element_blank())
                    }
                   )
plot_grid(plotlist = Acta2.plots, labels = samples)

Emcn (capillary endothelium):

Emcn.plots <- lapply(so.list.cutoff, function(i) {
  FeaturePlot(object = i, features = "Emcn", reduction = "umap") +
    theme_minimal(base_size = 8) +
    theme(title = element_blank())
}
)
plot_grid(plotlist = Emcn.plots, labels = samples)

Emr1 (macrophage):

Emr1.plots <- lapply(so.list.cutoff, function(i) {
  FeaturePlot(object = i, features = "Adgre1", reduction = "umap") +
    theme_minimal(base_size = 8) +
    theme(title = element_blank())
}
)
plot_grid(plotlist = Emr1.plots, labels = samples)

Doublet prediction

I’m using DoubletFinder which is compares real cells to simulated doublet transcriptomes. Here I am ignoring homotypic doublets- for this basic overview it is probably not going to impact results.

In a real-world application with more time I would try to estimate homotypic doublet rates from ground-truth cell-type proportions to ID those candidates as well

estimate of doublet rate from Tabula Muris paper- “Cells were loaded in each channel with a target output of 5,000 cells per sample” at 5,000 cells expected doublet rate is ~2.4%

perform parameter sweeping of pK (neighborhood size) with range of vals for pN (number artificial doublets)

sweep.res.list <- lapply(so.list.cutoff, # parameter sweeping neighborhood size
                         function(i) {
                           paramSweep_v3(i, PCs = PCs, sct = TRUE)
                         }
                        )

sweep.stats <- lapply(sweep.res.list,  # get summary of parameter sweeps. ROC analysis
                      function(i) {
                        summarizeSweep(i, GT = FALSE)
                      }
                    )
bcmvn <- lapply(sweep.stats, find.pK) # get AUC in ROC analysis for each pK

pKmax <- lapply(bcmvn, function(i) { i[which.max(i$BCmetric),]$pK } ) # get the max pK
pKmax <- as_vector(type.convert(pKmax, as.is = TRUE))

looking at our pK values that maximize AUC

print(pKmax)
  mo3_M_8   mo3_M_9  mo3_F_57 mo18_F_50 mo18_F_51 mo18_M_52 
     0.03      0.15      0.03      0.09      0.01      0.09 

expecting 2.4% doublets based on 10X guide and 5000 cells loaded

nExp <- lapply(so.list.cutoff, function(i) { round(ncol(i) * 0.024) })

run doubletfinder:

# for each sample, using individual pKmax and constant doublet rate
so.filtered <- mapply(function(obj, pKm, exp_doub) {
                        doubletFinder_v3(seu = obj, 
                                         pN = 0.25, 
                                         pK = pKm, 
                                         nExp = as.numeric(exp_doub), 
                                         PCs = PCs,
                                         sct = TRUE)
                              },
                        so.list.cutoff, pKmax, nExp
                      )

visualize doublet properties. We expect doublets to have a higher number of features than average.

dblt.umap <- mapply(function(obj, prediction) {
                        DimPlot(obj,
                                group.by = prediction, 
                                pt.size = 0.4, 
                                reduction = "umap",
                                order = "Doublet") + 
                        labs(title = element_blank())
                      },
                      so.filtered, DF.name,
                      SIMPLIFY = FALSE
                      )

plot_grid(plotlist = dblt.umap, ncol = 2, labels= samples)

not seeing a clear trend of greater counts and features in doublets. this is not required of a doublet but expect see a trend. A biological explanation would be that some cell types could express many more features than others. With more time this would be an area that I would want to troubleshoot

remove doublets:

so.nodb <- mapply(function(obj, prediction) {
              obj[, obj@meta.data[, prediction] == "Singlet"] # select barcodes predicted to be singlets
             },
            so.filtered, DF.name, 
            SIMPLIFY = FALSE
)

batch correction and data integration

batch correction using Comprehensive Integration Algorithm (Stuart et al 2019), canonical correlation analysis is used to capture features that correlate among batches, then mutual nearest neighbors are found to make a graph of all samples

prepare the data:

# add names to each sample dataset
so.nodb <- mapply(function(x, y) {
                         AddMetaData(object = x,
                                     metadata = y,
                                     col.name = "sample")
                            },
                      so.nodb, samples,
                      SIMPLIFY = FALSE
                    )

# add age and sex information
so.nodb$mo3_M_8   %<>% AddMetaData(metadata = "3",  col.name = "age")
so.nodb$mo3_M_9   %<>% AddMetaData(metadata = "3",  col.name = "age")
so.nodb$mo3_F_57  %<>% AddMetaData(metadata = "3",  col.name = "age")
so.nodb$mo18_F_50 %<>% AddMetaData(metadata = "18", col.name = "age")
so.nodb$mo18_F_51 %<>% AddMetaData(metadata = "18", col.name = "age")
so.nodb$mo18_M_52 %<>% AddMetaData(metadata = "18", col.name = "age")
so.nodb$mo3_M_8   %<>% AddMetaData(metadata = "M",  col.name = "sex")
so.nodb$mo3_M_9   %<>% AddMetaData(metadata = "M",  col.name = "sex")
so.nodb$mo3_F_57  %<>% AddMetaData(metadata = "F",  col.name = "sex")
so.nodb$mo18_F_50 %<>% AddMetaData(metadata = "F",  col.name = "sex")
so.nodb$mo18_F_51 %<>% AddMetaData(metadata = "F",  col.name = "sex")
so.nodb$mo18_M_52 %<>% AddMetaData(metadata = "M",  col.name = "sex")

identify the shared variable features among datasets and specifically prepare to use integration with SCTransform method

shared.var.features <- SelectIntegrationFeatures(object.list = so.nodb, nfeatures = 3000)
so.nodb %<>% PrepSCTIntegration(anchor.features = shared.var.features, 
                                verbose = FALSE)

identify integration anchors and integrate the data

anchors <- FindIntegrationAnchors(object.list = so.nodb, 
                                  normalization.method = "SCT", 
                                  anchor.features = shared.var.features, verbose = FALSE)
kidney.integrated <- IntegrateData(anchorset = anchors, 
                                   normalization.method = "SCT", verbose = FALSE)

remove some big objects we no longer need

rm(anchors, so.list.cutoff, so.filtered, so.list.mitofilter, so.list, sweep.res.list)

now analyze combined dataset

DefaultAssay(kidney.integrated) <- "integrated" # this just sets the default slot in the seurat object to be the integrated data rather than un-corrected data

dimensionality reduction and clustering of integrated data

the counts data is already normalized and batch-corrected from the upstream processing. however now that we have all samples combine we will need to re-run PCA, make a new SNN graph, find clusters and perform UMAP again

PCA:

kidney.integrated %<>% RunPCA()
PC_ 1 
Positive:  Ly6c1, Tm4sf1, Slc9a3r2, Ly6a, Emcn, Hspb1, Egfl7, Plpp1, Pecam1, Klf2 
       Crip2, Flt1, Ly6e, Meis2, Adgrl4, Ifitm3, Eng, Ptprb, Gimap6, Cd200 
       Rsad2, Rhob, Icam2, Cd24a, Sparc, Gng11, Plpp3, Tinagl1, Ehd4, Esam 
Negative:  Spink1, Miox, Aldob, Slc34a1, Akr1c21, Pck1, Gpx3, Ass1, Gpx1, Cltrn 
       Slc27a2, Gsta2, Rida, Gatm, Ttc36, Acsm2, Hao2, Dbi, Nat8f1, Fbp1 
       Lrp2, Acaa1b, Rdh16f2, Ndrg1, Guca2b, Slc4a4, Dnase1, Pah, Khk, Keg1 
PC_ 2 
Positive:  Cd52, Laptm5, Coro1a, Tyrobp, Ctss, Fcer1g, Lyz2, Cd53, Lcp1, Ctsc 
       Ptprc, Spi1, Ucp2, Fyb, Rgs10, Ly86, Cd74, H2-Eb1, H2-Aa, Pld4 
       Unc93b1, Lst1, Cybb, Sh3bgrl3, Alox5ap, H2-DMa, Rgs2, Cst3, H2-Ab1, Ptpn18 
Negative:  Spink1, Miox, Aldob, Slc34a1, Pck1, Akr1c21, Gpx3, Ass1, Slc27a2, Cltrn 
       Gsta2, Timp3, Rida, Acsm2, Ttc36, Ndrg1, Hao2, Tm4sf1, Gatm, Gpx1 
       Nat8f1, Lrp2, Slc9a3r2, Acaa1b, Fbp1, Sgk1, Dbi, Id1, Emcn, Pecam1 
PC_ 3 
Positive:  Ppp1r1a, Tmem213, Egf, Wfdc15b, Defb1, Kng2, Umod, Nudt4, Klk1, Wfdc2 
       Tmem52b, Mt1, Kcnj1, Atp1a1, Atp1b1, Slc16a7, Sostdc1, Clcnkb, Mal, Slc12a1 
       Mt2, Epcam, Sfrp1, Ckb, Hoxd8, Scd2, Abca13, Tmem72, Pgam2, Wnk1 
Negative:  B2m, H2-D1, Ifitm3, Srgn, Gpx3, Tmsb4x, Psmb8, Slc34a1, Miox, Spink1 
       Klf2, H2-K1, Fxyd5, Gm8995, Cd74, Bst2, Aldob, H2-Ab1, Ly6e, Arpc1b 
       Cd52, Gatm, Ehd4, Tyrobp, Laptm5, Apoe, Cltrn, Tgfb1, Ctss, H2-Eb1 
PC_ 4 
Positive:  Ly6a, Egf, Kng2, Ppp1r1a, Klk1, Tmem52b, Wfdc15b, Wnk1, Atp1a1, Tmem213 
       Umod, Ly6c1, Slc12a3, Defb1, Hao2, Kcnj1, Slc27a2, Kdr, Wfdc2, Acaa1b 
       Ckb, Abca13, Bst2, Plvap, Fxyd2, Cyp4a10, Rdh16f2, Sfrp1, Emcn, Flt1 
Negative:  Vim, Cryab, Junb, Tpm1, 4930523C07Rik, S100a11, Nr4a1, Tmsb10, Bcam, Cpe 
       Cald1, Fos, Egr1, Actn1, Rasl11a, Nbl1, Timp2, Nupr1, Akap12, Cavin1 
       Btg2, Fosb, Fxyd1, Ppp1r15a, Ier2, Btg1, Cebpb, Col18a1, Jund, Lgals1 
PC_ 5 
Positive:  Rplp0, Rps15a, Ptprcap, Rps13, Rpsa, Gimap6, Sept1, Rps11, Rpl32, Rpl17 
       Rps7, Rps19, Dusp5, Ltb, Trbc2, Rplp2, Gm11478, Rps18, Rps16, Rps20 
       Cd3g, B4galnt1, Lck, Vps37b, Rps11-ps2, Shisa5, Rpl13, Rps24, Rps6, Gm9844 
Negative:  Apoe, Fos, C1qb, C1qc, Egr1, Axl, C1qa, Dusp1, Jun, Atf3 
       Cd14, Csf1r, Cxcl16, Mafb, Ms4a7, Lilra5, Fosb, Trf, H2-DMb1, Cd72 
       Btg2, Adgre1, Hexb, Cfh, Slamf9, C3ar1, Fcgr3, Fcgr4, Timp2, Cst3 
kidney.integrated %>% ElbowPlot(ndims = 40, reduction = "pca")

PC27 looks like a good break point

PCs = 1:27

plot PCA:

PCA.12.plot <- DimPlot(kidney.integrated, # PC1 vs PC2
                       dims = c(1,2), 
                       reduction = "pca", 
                       group.by = "age", 
                       shuffle = TRUE,
                       pt.size = 0.3) +
                       theme(plot.title = element_blank(),
                             text = element_text(size = 11)) +
                       labs(color = "age (mo)") #%>% adjust.alpha(a = 0.8)
PCA.13.plot <- DimPlot(kidney.integrated,
                       dims = c(1,3), 
                       reduction = "pca", 
                       group.by = "age",
                       shuffle = TRUE,
                       pt.size = 0.3) +
                       theme(plot.title = element_blank(),
                             text = element_text(size = 11))+
                       labs(color = "age (mo)")#%>% adjust.alpha(a = 0.8)
PCA.12.plot %<>% adjust.alpha(a = 0.8)
PCA.13.plot %<>% adjust.alpha(a = 0.8)
plot_grid(PCA.12.plot, PCA.13.plot)

create shared nearest neighbor graph:

kidney.integrated %<>% FindNeighbors(reduction = "pca", dims = PCs)
Computing nearest neighbor graph
Computing SNN

do Leiden clustering at a series of resolutions:

kidney.integrated %<>% FindClusters(
                          algorithm = "leiden",
                          resolution = seq(0.2,1.4,0.1),
                          verbose = FALSE)
print(sapply(
        grep("res", colnames(kidney.integrated@meta.data), value = TRUE),
        function(i) { length(unique(kidney.integrated@meta.data[,i])) }
        )
)
       SCT_snn_res.0.8 integrated_snn_res.0.2 integrated_snn_res.0.3 
                    14                     14                     15 
integrated_snn_res.0.4 integrated_snn_res.0.5 integrated_snn_res.0.6 
                    19                     21                     23 
integrated_snn_res.0.7 integrated_snn_res.0.8 integrated_snn_res.0.9 
                    23                     23                     25 
  integrated_snn_res.1 integrated_snn_res.1.1 integrated_snn_res.1.2 
                    27                     30                     31 
integrated_snn_res.1.3 integrated_snn_res.1.4 
                    31                     33 

based on the tabula muris senis annotation we expect around 18 cell types total. choose resolution that gives 19 clusters, they can be merged later on.

Idents(kidney.integrated) <- "integrated_snn_res.0.4"

Do UMAP. using 20 nearest neighbors to get better local structure, which is helpful for resolving similar cell types

kidney.integrated %<>% RunUMAP(reduction = "pca",
                               dims = PCs,       # running UMAP on PCs 1-27
                               n.neighbors = 20, 
                               verbose = FALSE)
p1 <- DimPlot(kidney.integrated,  #plot 1: color points by sample
              reduction = "umap", 
              group.by = "sample", 
              pt.size = 0.2, 
              shuffle = T) +
            scale_color_brewer(palette = "Dark2") + 
                theme(plot.title = element_blank(), 
                      axis.title.x = element_text(size = 9),
                      axis.title.y = element_text(size = 9), 
                      legend.text = element_text(size = 9),
                      axis.text = element_text(size = 9))

p2 <- DimPlot(kidney.integrated,   # plot 2: color points by age
              reduction = "umap", 
              group.by = "age", 
              pt.size = 0.2, 
              shuffle = T) +
                theme(plot.title = element_blank(), 
                      axis.title.x = element_text(size = 9),
                      axis.title.y = element_text(size = 9), 
                      legend.text = element_text(size = 9),
                      axis.text = element_text(size = 9))

p3 <- DimPlot(kidney.integrated,   # plot 3: color points by leiden clusters (stored as 'ident' in seurat object)
              reduction = "umap", 
              group.by = 'ident',
              pt.size = 0.2,
              shuffle = TRUE) + 
                theme(plot.title = element_blank(), 
                                   axis.title.x = element_text(size = 9),
                                   axis.title.y = element_text(size = 9),
                                   legend.text = element_text(size = 9),
                                   axis.text = element_text(size = 9)) 

p1 %<>% adjust.alpha(a = 0.7) # adjust alpha for each plot
p2 %<>% adjust.alpha(a = 0.7)
p3 %<>% adjust.alpha(a = 0.9)

plot_grid(p1, p2, p3, labels = c('sample', 'age', 'leiden clusters'), ncol = 1)

so we can see that there is concordant clustering among batches into shared clusters, with some differences breaking down by condition (expected!)

annotate cell types

I was hoping to annotate automatically by training a model on some of the other ages of TMS data, but I ran into trouble converting the Anndata python format into a format that is useable in R:

# first i create a conda environment locally then:
#reticulate::use_condaenv("anndata") # python via reticulate
#ad <- reticulate::import("anndata", convert = FALSE) # importing anndata python class
#kidney_ad <- ad$read_h5ad("tabula-muris-senis-droplet-processed-official-annotations-Kidney.h5ad")
#kidney.tms <- Convert(kidney_ad, to = "seurat")

unfortunately cannot get reticulate to work as expected here.

Instead I annotated manually by comparing marker genes of my clusters to expression in the TMS cellxgene browser.

The first step is to find cell type markers that define clusters (conserved across ages and samples) using wilcox rank sum tests. to find the top markers, I required that markers have logfoldchange >= 0.25, and defined by presence (positive markers) rather than absence (negative markers). this process compares expression in each cluster to expression across all other clusters, i.e. clust_i vs (all - clust_i):

# the function below replaces the individually transformed values for each batch with a # single sequencing depth covariate.

#kidney.integrated %<>% PrepSCTFindMarkers()

markers_all <- FindAllMarkers(      # clust_i vs (all - clust_i) wilcox rank sum test
  object = kidney.integrated,
  only.pos = TRUE,  
  logfc.threshold = 0.25, verbose = FALSE
)

dim(markers_all)[1] # how many markers?
[1] 26860

get top 15 markers for each cluster:

top5 <- lapply(unique(markers_all$cluster),
                function(i) {
                head(subset(markers_all, cluster == i), n=5)
              }
            )
# here is an example:
print(top5[1])
[[1]]
                  p_val avg_log2FC pct.1 pct.2     p_val_adj cluster      gene
Lrp2      8.438711e-111  38.419721 0.925 0.435 1.472133e-106       1      Lrp2
Slc27a2   4.510424e-106  11.451090 0.889 0.476 7.868434e-102       1   Slc27a2
mt-Rnr1    4.693023e-98 181.688927 0.991 0.859  8.186978e-94       1   mt-Rnr1
Serpina1f  7.019036e-86  24.249213 0.119 0.009  1.224471e-81       1 Serpina1f
Slc13a3    2.128109e-80   5.416399 0.645 0.293  3.712486e-76       1   Slc13a3

I compared marker expression in this dataset with the TMS in cellxgene. here is an example of visualization of brush cell markers:

now assign all markers. I collapsed some clusters together and wasn’t able to call every cell in the TMS data.

kidney.integrated %<>% RenameIdents("1" = "brush_cell",
                                    "2" = "brush_cell",
                                    "3" = "proximal_tubule_cell",
                                    "4" = "fenestrated_cell",
                                    "5" = "proximal_tubule_cell",
                                    "6" = "distal_convoluted_tubule_cell",
                                    "7" = "thin_LOH",
                                    "8" = "thick_LOH",
                                    "9" = "macrophage",
                                    "10" = "proximal_tubule_cell",
                                    "11" = "fibroblast",
                                    "12" = "T_cell",
                                    "13" = "collecting_duct_principal_cell",
                                    "14" = "NK_cell",
                                    "15" = "podocyte",
                                    "16" = "B_cell",
                                    "17" = "capillary_endothelial_cell",
                                    "18" = "distal_convoluted_tubule_cell",
                                    "19" = "mesangial_cell")

plot UMAP again with cell type annotation:

mycols <- colorRampPalette(brewer.pal(12, "Paired"))(15) # expanding this palette from 12 to 15 colors

pUMAP <- DimPlot(kidney.integrated, 
              reduction = "umap", 
              pt.size = 0.2,
              shuffle = TRUE) + 
  scale_color_manual(values = mycols) +
  theme(plot.title = element_blank(), 
        axis.title.x = element_text(size = 9),
        axis.title.y = element_text(size = 9),
        legend.text = element_text(size = 9),
        axis.text = element_text(size = 9)) %>% adjust.alpha(a = 0.9)

pUMAP

changes in cell type composition with age

plot cell type ratios per sample:

celltypeXsample <- as.data.frame(table(Idents(kidney.integrated), # gather up number of each cell type per sample
                                       kidney.integrated$sample)) %>% 
                                          setNames(c("cell_type", "sample", "counts"))

celltypeXsample$sample <- factor(celltypeXsample$sample, levels = samples) # order the sample names

celltype_abund_plot <- ggplot(celltypeXsample, aes(sample, counts, fill = cell_type)) + 
                           geom_bar(position="fill", stat="identity") + # position = 'fill' normalizes scale of each sample
                           scale_fill_manual(values = mycols) +
                           theme_minimal(base_size =  11) +
                           labs(y = "relative abundance",
                                fill = "cell type") +
                           theme(axis.title.x = element_blank()) 

celltype_abund_plot

for a statistical test of abundances across age we need a two-sample test that can account for sex as a random variable. the package speckle/propeller. does a moderated t-test on transformed proportional data and is very easy to apply to a seurat object. https://www.biorxiv.org/content/10.1101/2021.11.28.470236v1.full

kidney.integrated$celltype <- Idents(kidney.integrated)

# transformed cell type proportions: 
props <- speckle::getTransformedProps(kidney.integrated$celltype,
                                      kidney.integrated$sample,
                                      transform = 'logit')
Performing logit transformation of proportions
# make a design matrix that accounts for age and sex:
age <- c(rep('18', 3), rep('3', 3))
sex <- c("M", "M", "F", "F", "F", "M")
data.frame(age, sex)
design <- model.matrix(~ 0 + age + sex)
mycontr <- makeContrasts(age18-age3, levels=design)
mycontr # contrasts age with sex as a random effect
       Contrasts
Levels  age18 - age3
  age18            1
  age3            -1
  sexM             0

implement empirical bayes moderated ttest. here a fraction >1 indicates more of that cell type in age 18:

print(propeller.ttest(prop.list = props,
                         design = design,
                         robust = TRUE,
                         trend = FALSE,
                         sort = TRUE, 
                         contrasts = mycontr))

we can see that natural killer cells and thin loop of henle are differently abundant with a significant p value but the FDR is 11% and 36% respectively. so no differences we can confident in when accounting for multiple testing. Additionally we can see that there are many more T and B cells in 18 month old mice, but these differences are not significant either - likely because these cells are rare and have less power to detect differences.

differential expression

perform DE analysis on age among the two biggest clusters. these clusters are proximal tubule cells and brush cells:

table(Idents(kidney.integrated))

                    brush_cell           proximal_tubule_cell 
                          1480                           1490 
              fenestrated_cell  distal_convoluted_tubule_cell 
                           641                            616 
                      thin_LOH                      thick_LOH 
                           482                            421 
                    macrophage                     fibroblast 
                           320                            176 
                        T_cell collecting_duct_principal_cell 
                           174                            147 
                       NK_cell                       podocyte 
                           111                             87 
                        B_cell     capillary_endothelial_cell 
                            77                             68 
                mesangial_cell 
                            54 

create a new data class that has both cell type and age information:

kidney.integrated$celltype.age <- paste(kidney.integrated$celltype, 
                                        kidney.integrated$age,
                                           sep = "_")
Idents(kidney.integrated) <- "celltype.age"

we can conveniently implement MAST for sc DE from Seurat::FindMarkers()

# how many significant DE genes?
cat(paste0(dim(kidney.PTC.MAST)[1], " DE genes in PTC\n", 
             dim(kidney.BC.MAST)[1], " DE genes in BC"))
159 DE genes in PTC
233 DE genes in BC

observe the top 15 DE genes (ordered by absolute log2FC) for each cell type. A positive log2FC indicates greater expression in age 18mo vs 3mo

top15.PTC <- head(kidney.PTC.MAST[order(kidney.PTC.MAST$abs.LFC, decreasing = TRUE), ], n = 15)
top15.BC <- head(kidney.BC.MAST[order(kidney.BC.MAST$abs.LFC, decreasing = TRUE), ], n = 15)

top 15 DE in proximal tubule cells:

top15.PTC

top 15 DE in brush cells:

top15.BC

how much overlap is there between the significantly DE genes in these cells

shared.DE <- rownames(kidney.BC.MAST)[rownames(kidney.BC.MAST) %in% rownames(kidney.PTC.MAST)]
length(shared.DE)

93 genes in common. makes sense as these are very similar cell types!

visualize a few of the top DE genes:

# we need to normalize the count data in the RNA slot for DE visualization
kidney.integrated %<>% NormalizeData(assay = "RNA")
PTC.DE.vln <- VlnPlot(kidney.integrated,
                      assay = "RNA",
                      slot = "data",
                      features= head(rownames(top15.PTC)),
                      idents = c("proximal_tubule_cell_3", "proximal_tubule_cell_18")) &
                        theme(axis.title.x = element_blank())
BC.DE.vln <- VlnPlot(kidney.integrated,
                     assay = "RNA",
                     slot = "data",
                     features= head(rownames(top15.BC)),
                     idents = c("brush_cell_3", "brush_cell_18")) &
                        theme(axis.title.x = element_blank())

plot_grid(PTC.DE.vln, BC.DE.vln, ncol = 1)

pathway analysis of DE genes

we can use package clusterProfiler to do pathway enrichment against online KEGG database

search_kegg_organism('mmu', by='kegg_code')

prepare gene lists for enrichment analysis. we need to convert gene symbols to entrez IDs to search KEGG

# DE gene lists
PTC.genelist <- rownames(kidney.PTC.MAST)
BC.genelist <- rownames(kidney.BC.MAST)

# background gene lists (all genes expressed in focal cell type)
Idents(kidney.integrated) <- "celltype"

PTC.totalcounts <- rowSums(subset(kidney.integrated, idents = "proximal_tubule_cell")@assays$RNA@counts)
PTC.background <- names(PTC.totalcounts[PTC.totalcounts > 100]) # define 'expressed' as > 100 total counts per gene
BC.totalcounts <- rowSums(subset(kidney.integrated, idents = "brush_cell")@assays$RNA@counts)
BC.background <- names(BC.totalcounts[BC.totalcounts > 100]) # define 'expressed' as > 100 total counts per gene

# first convert to ensembl IDs using the genes.tsv data supplied for this assignment. 
# we get a few more matches to entrez going from ensembl rather than gene symbols.
mouse_genes <- read.table("3-M-8/genes.tsv") 

# match symbols in gene lists to the symbol+ensembl table
PTC.genelist <- left_join(data.frame(PTC.genelist), mouse_genes, by = c("PTC.genelist" = "V2"))
BC.genelist <- left_join(data.frame(BC.genelist), mouse_genes, by = c("BC.genelist" = "V2"))
PTC.background <- left_join(data.frame(PTC.background), mouse_genes, by = c("PTC.background" = "V2"))
BC.background <- left_join(data.frame(BC.background), mouse_genes, by = c("BC.background" = "V2"))

# now get entrez IDs from ensembl IDs
IDs <- lapply(list(PTC.genelist, 
                    BC.genelist, 
                    PTC.background, 
                    BC.background), 
               function(i) {select(org.Mm.eg.db,   # this searches the ncbi mouse gene database and returns gene aliases
                            keys = unlist(lapply(str_split(i$V1, '\\.'), function(x) x[1])),
                            columns = c("ENTREZID", "ENSEMBL", "SYMBOL"), # return entrez, ensembl, and gene symbol
                            keytype = "ENSEMBL") # searching with ensembl IDs
               }
)

# and now list the entrez ids that were found (excluding the NAs that couldnt be found in the database)
PTC.genelist.entrez <- data.frame(IDs[1])$ENTREZID[!is.na(data.frame(IDs[1])$ENTREZID)]
BC.genelist.entrez <- data.frame(IDs[2])$ENTREZID[!is.na(data.frame(IDs[2])$ENTREZID)]
PTC.background.entrez <- data.frame(IDs[3])$ENTREZID[!is.na(data.frame(IDs[3])$ENTREZID)]
BC.background.entrez <- data.frame(IDs[4])$ENTREZID[!is.na(data.frame(IDs[4])$ENTREZID)]

now search the KEGG database for significant pathway enrichment in proximal tubule cell DE genes:

PTC.kegg <- enrichKEGG(gene = PTC.genelist.entrez,
                       universe = PTC.background.entrez,
                       organism  = 'mmu',
                       pvalueCutoff = 0.05)

and brush cell DE genes:

BC.kegg <- enrichKEGG(gene = BC.genelist.entrez,
                       universe = BC.background.entrez,
                       organism  = 'mmu',
                       pvalueCutoff = 0.05)

results for proximal tubule cell pathways:

print.data.frame(head(PTC.kegg))
               ID                           Description GeneRatio  BgRatio
mmu03010 mmu03010                              Ribosome    45/112 130/3595
mmu05171 mmu05171        Coronavirus disease - COVID-19    46/112 142/3595
mmu00830 mmu00830                    Retinol metabolism     5/112  24/3595
mmu05204 mmu05204 Chemical carcinogenesis - DNA adducts     5/112  25/3595
               pvalue     p.adjust       qvalue
mmu03010 2.719024e-38 4.160106e-36 3.949740e-36
mmu05171 1.098663e-37 8.404773e-36 7.979763e-36
mmu00830 7.101772e-04 3.312050e-02 3.144568e-02
mmu05204 8.658955e-04 3.312050e-02 3.144568e-02
                                                                                                                                                                                                                                                                                                  geneID
mmu03010       22186/20090/67671/20115/66481/66489/67248/20088/110954/56040/20116/100040298/67427/67281/100502825/19989/20102/20042/76808/11837/19933/68436/619547/65019/78294/68052/19981/57294/66475/75617/26451/20068/268449/67186/20055/19941/19934/19946/57808/27050/319195/27207/20104/19988/19943
mmu05171 22186/20090/67671/20115/66481/66489/67248/20088/110954/56040/20116/100040298/67427/67281/100502825/19989/20102/20042/76808/11837/19933/68436/619547/65019/78294/68052/19981/57294/66475/75617/26451/20068/268449/67186/20055/99571/19941/19934/19946/57808/27050/319195/27207/20104/19988/19943
mmu00830                                                                                                                                                                                                                                                                100559/216454/20148/13086/226143
mmu05204                                                                                                                                                                                                                                                                 100559/14860/14859/12408/226143
         Count
mmu03010    45
mmu05171    46
mmu00830     5
mmu05204     5

and brush cells:

print.data.frame(head(BC.kegg))
               ID                    Description GeneRatio  BgRatio       pvalue
mmu05171 mmu05171 Coronavirus disease - COVID-19    26/155 143/3711 6.796800e-11
mmu03010 mmu03010                       Ribosome    24/155 130/3711 2.890053e-10
mmu04714 mmu04714                  Thermogenesis    22/155 176/3711 2.408044e-06
mmu00190 mmu00190      Oxidative phosphorylation    16/155 116/3711 1.842738e-05
mmu00830 mmu00830             Retinol metabolism     7/155  27/3711 8.493003e-05
mmu04260 mmu04260     Cardiac muscle contraction     8/155  36/3711 8.624706e-05
             p.adjust       qvalue
mmu05171 1.556467e-08 1.488141e-08
mmu03010 3.309111e-08 3.163848e-08
mmu04714 1.838140e-04 1.757450e-04
mmu00190 1.054968e-03 1.008657e-03
mmu00830 3.291763e-03 3.147261e-03
mmu04260 3.291763e-03 3.147261e-03
                                                                                                                                                                    geneID
mmu05171 22186/16476/66481/26451/57294/110954/14281/20115/20102/267019/66483/20104/67248/19988/67671/66480/67281/100502825/19942/11837/66475/27050/66489/67186/20091/57808
mmu03010             22186/66481/26451/57294/110954/20115/20102/267019/66483/20104/67248/19988/67671/66480/67281/100502825/19942/11837/66475/27050/66489/67186/20091/57808
mmu04714                              17705/17710/17709/17711/17719/11465/17716/22273/14081/23945/17717/11461/22272/20104/66916/27425/17721/78330/66594/225887/57279/12859
mmu00190                                                                  17705/17710/17709/17711/17719/17716/22273/17717/22272/66916/27425/17721/78330/66594/225887/12859
mmu00830                                                                                                                      13086/11668/216454/20148/100559/226143/19683
mmu04260                                                                                                                   17710/17709/17711/22273/11936/22272/66594/12859
         Count
mmu05171    26
mmu03010    24
mmu04714    22
mmu00190    16
mmu00830     7
mmu04260     8
LS0tCnRpdGxlOiAiVGFidWxhIE11cmlzIFNlbmlzIEtpZG5leSBBbmFseXNpcyIKYXV0aG9yOiAiQWxleCBNYWphbmUiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+CiAgYm9keXsKICBmb250LXNpemU6IDEycHQ7Cn0KPC9zdHlsZT4KCmBgYHtyIHNldHVwfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIGNhY2hlID0gVFJVRSkKYGBgCgpgYGB7cn0KbGlicmFyeShNYXRyaXgpCmxpYnJhcnkoRHJvcGxldFV0aWxzKQpsaWJyYXJ5KFNvdXBYKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoY293cGxvdCkKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkobWFncml0dHIpCmxpYnJhcnkoU2V1cmF0KQpsaWJyYXJ5KERvdWJsZXRGaW5kZXIpCmxpYnJhcnkocmV0aWN1bGF0ZSkKbGlicmFyeShzY1ByZWQpCmxpYnJhcnkoU2V1cmF0RGlzaykKbGlicmFyeShzcGVja2xlKQpsaWJyYXJ5KFJDb2xvckJyZXdlcikKbGlicmFyeShsaW1tYSkKbGlicmFyeShNQVNUKQpsaWJyYXJ5KGNsdXN0ZXJQcm9maWxlcikKbGlicmFyeShvcmcuTW0uZWcuZGIpCgpzZXR3ZCgifi9Eb2N1bWVudHMvQmVndW4vdG1zXzNfYW5kXzE4bV9kcm9wbGV0X2tpZG5leS8iKQpgYGAKCiMjIHJlYWQgcmF3IG1hdHJpY2VzIGFuZCBzZWxlY3QgY2VsbHMgZnJvbSBlbXB0eSBkcm9wcwoKYGBge3IgY2FjaGUgPSBUUlVFfQpyZWFkLnNjIDwtIGZ1bmN0aW9uKHgpIHsgCiAgbXlkYXRhIDwtIHJlYWQxMHhDb3VudHMocGFzdGUwKHgsICIvIikpICAjIHJlYWQgY2VsbHJhbmdlciBvdXRwdXQKICBteWRhdGEKfQoKc2FtcGxlcyA8LSBjKCIzLU0tOCIsICAgICAjIHNhbXBsZSBuYW1lcwogICAgICAgICAgICAgIjMtTS05IiwKICAgICAgICAgICAgICIzLUYtNTciLAogICAgICAgICAgICAgIjE4LUYtNTAiLAogICAgICAgICAgICAgIjE4LUYtNTEiLAogICAgICAgICAgICAgIjE4LU0tNTIiKQoKc2NlLmxpc3QgPC0gc2FwcGx5KHNhbXBsZXMsIHJlYWQuc2MpICMgaW1wb3J0IGFsbCBzaXggc2FtcGxlcyBhbmQgc3RvcmUgaW4gYSBsaXN0IG9mIFNpbmdsZSBDZWxsIEV4cGVyaW1lbnRzCgojIGlsbGVnYWwgUiBjaGFyYWN0ZXJzOiB0cmFuc2xhdGUgIi0iIHRvICJfIiwgYW5kIGRvIG5vdCBzdGFydCBuYW1lIHdpdGggYSBudW1iZXIKbmFtZXMoc2NlLmxpc3QpIDwtIGNoYXJ0cigiLSIsICJfIiwgcGFzdGUwKCJtbyIsIHNhbXBsZXMpKQpuYW1lcyhzY2UubGlzdCkgCmBgYAoKY2FsY3VsYXRlIGJhcmNvZGUgcmFua3MgYW5kIHZpc3VhbGl6ZSB3aXRoIHdhdGVyZmFsbCBwbG90czoKYGBge3J9CmJyIDwtIGxhcHBseShzY2UubGlzdCwgYmFyY29kZVJhbmtzKSAjIGZ1bmN0aW9uIHRoYXQgcmFua3MgYmFyY29kZXMgYnkgI1VNSXMKc2FwcGx5KGJyLCBmdW5jdGlvbihpKSB7aUBtZXRhZGF0YX0pICMgc2hvdyBrbmVlIGFuZCBpbmZsZWN0aW9uIHBvaW50CmBgYAoKYGBge3IgZmlnLndpZHRoPTgsIGZpZy5oZWlnaHQ9Nn0Kd2F0ZXJmYWxsX3Bsb3QgPC0gZnVuY3Rpb24ocykgewogIGJyLmRmIDwtIGFzLmRhdGEuZnJhbWUocykgICAKICBici5kZiAlPD4lIGRpc3RpbmN0KHRvdGFsLCAua2VlcF9hbGwgPSBUUlVFKSAjIHJlbW92ZSByZWR1bmRhbnQgZGF0YXBvaW50cyB0byByZWR1Y2Ugc2l6ZSB0aGF0IGhhcyB0byBnbyB0byBtZW1vcnkgd2hlbiBwbG90dGluZwogIGJyLnBvaW50cyA9IGRhdGEuZnJhbWUoCiAgICBwb2ludCA9IGMoImtuZWUiLCAiaW5mbGVjdGlvbiIpLAogICAgdmFsID0gYyhzQG1ldGFkYXRhJGluZmxlY3Rpb24sIHNAbWV0YWRhdGEka25lZSkpCiAgZ2dwbG90KGRhdGEgPSBici5kZiwgYWVzKHggPSByYW5rLCB5ID0gKHRvdGFsICsgMSkpKSArCiAgICBnZW9tX3BvaW50KHNpemUgPSAwLjc1KSArCiAgICBnZW9tX2hsaW5lKGRhdGEgPSBici5wb2ludHMsCiAgICAgICAgICAgICAgIGFlcyh5aW50ZXJjZXB0ID0gdmFsLCBjb2xvdXIgPSBwb2ludCksIGxpbmV0eXBlID0gMikgKwogICAgc2NhbGVfeF9sb2cxMCgpICsKICAgIHNjYWxlX3lfbG9nMTAoKSArCiAgICBsYWJzKHggPSAibG9nKGJhcmNvZGUgcmFuaykiLCB5ID0gImxvZyhVTUkgY291bnRzKSIpICsKICAgIHRoZW1lX21pbmltYWwoYmFzZV9zaXplID0gNykKfQoKYnIud2F0ZXJmYWxsX3Bsb3RzIDwtIGxhcHBseShiciwgd2F0ZXJmYWxsX3Bsb3QpCgpwbG90X2dyaWQocGxvdGxpc3QgPSBici53YXRlcmZhbGxfcGxvdHMsIAogICAgICAgICAgbGFiZWxzID0gc2FtcGxlcywgCiAgICAgICAgICBsYWJlbF9zaXplID0gNykgIyBwbG90IGFsbCBzaXggc2FtcGxlcwpgYGAKCnVzZSB0aGUgRW1wdHlEcm9wcyBhbGdvcml0aG0gdG8gcHJlZGljdCBwdXRhdGl2ZSBjZWxsczoKYGBge3IgY2FjaGUgPSBUUlVFfQpzZXQuc2VlZCg4KQplZC5vdXQgPC0gc2FwcGx5KHNjZS5saXN0LCBmdW5jdGlvbihpKSB7IGVtcHR5RHJvcHMoaSwgbG93ZXIgPSAyMDApIH0pICMgcmVxdWlyaW5nIGF0IGxlYXN0IDIwMCBVTUkKCiMgZmlsdGVyIHRvIGNlbGxzIHdpdGggRkRSIG9mIDAuMDEKY2VsbHMgPC0gbGFwcGx5KGVkLm91dCwgZnVuY3Rpb24oaSkgeyB3aGljaChpJEZEUiA8PSAwLjAxKSB9ICkKCiNzdWJzZXQgU0NFcyB3aXRoIGNlbGwgaW5kaWNlcwpzY2UuZmlsdGVyZWQubGlzdCA8LSBtYXBwbHkoZnVuY3Rpb24oeCwgeSkgeyB4Wyx5XSB9LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgc2NlLmxpc3QsIGNlbGxzKQpgYGAKCmBgYHtyIGZpZy53aWR0aD04LCBmaWcuaGVpZ2h0PTZ9CiMgdGFrZSBhIGxvb2sgYXQgd2F0ZXJmYWxsIHBsb3RzIGZvciB0aGUgY2VsbHMgcHJlZGljdGVkIGJ5IEVtcHR5RHJvcHMKYnIuZmlsdGVyZWQgPC0gbGFwcGx5KHNjZS5maWx0ZXJlZC5saXN0LCBiYXJjb2RlUmFua3MpCmJyLmZpbHRlcmVkLndhdGVyZmFsbF9wbG90cyA8LSBsYXBwbHkoYnIuZmlsdGVyZWQsIHdhdGVyZmFsbF9wbG90KQpwbG90X2dyaWQocGxvdGxpc3QgPSBici5maWx0ZXJlZC53YXRlcmZhbGxfcGxvdHMsIAogICAgICAgICAgbGFiZWxzID0gc2FtcGxlcywgCiAgICAgICAgICBsYWJlbF9zaXplID0gNykgIyBwbG90IGFsbCBzaXggc2FtcGxlcwpgYGAKCkVtcHR5RHJvcHMgcHJlZGljdHMgdGhhdCBtYW55IG9mIHRoZSBiYXJjb2RlcyBiZWxvdyB0aGUga25lZSBhcmUgcmVhbCBjZWxscyBiYXNlZCBvbiB0aGVpciB0cmFuc2NyaXB0b21lLiBUaGlzIGNhbiBiZSB1c2VmdWwgd2hlbiB0cnlpbmcgdG8gbWF6aW1pemUgdGhlIG51bWJlciBvZiBjZWxscyBjYXB0dXJlZCwgYnV0IG1hbnkgb2YgdGhlc2UgbWlnaHQgYmUgbG93IHF1YWxpdHkgY2VsbHMgLyBmcmFjdGlvbnMgb2YgY2VsbHMgLyBudWNsZWkuIFdlIGNhbiBmaWx0ZXIgdGhlc2Ugb3V0IGxhdGVyIHdpdGggYSBoYXJkIFVNSSBjdXRvZmYgYXJvdW5kIHRoZSBrbmVlIHBvaW50IG9mIH4xMDAwIFVNSS4KCmBgYHtyIGNhY2hlPVRSVUV9CiMjIGV4cG9ydCBmaWx0ZXJlZCBkYXRhCmRpci5jcmVhdGUoImZpbHRlcmVkIikKbGFwcGx5KG5hbWVzKHNjZS5maWx0ZXJlZC5saXN0KSwgIyBjcmVhdGUgZGlyZWN0b3J5IGZvciBlYWNoIGZpbHRlcmVkIHNhbXBsZQogICAgICAgZnVuY3Rpb24oaSkgewogICAgICAgZGlyLmNyZWF0ZShmaWxlLnBhdGgoImZpbHRlcmVkIiwgaSkpIH0gKQoKIyBub3cgd3JpdGUgaW4gMTBYIGZvcm1hdAptYXBwbHkoZnVuY3Rpb24oeCwgeSkgewogICAgICAgICB3cml0ZU1NKGNvdW50cyh4KSwgZmlsZSA9IGZpbGUucGF0aCgiZmlsdGVyZWQiLCB5LCAibWF0cml4Lm10eCIpKSAgIyBzcGFyc2UgbWF0cml4CiAgICAgICAgIHdyaXRlX2xpbmVzKHgkQmFyY29kZSwgZmlsZSA9IGZpbGUucGF0aCgiZmlsdGVyZWQiLCB5LCAiYmFyY29kZXMudHN2IikpICMgYmFyY29kZSBmaWxlCiAgICAgICAgIHdyaXRlLnRhYmxlKGFzLmRhdGEuZnJhbWUocm93RGF0YSh4KSksICMgZ2VuZXMgZmlsZQogICAgICAgICAgICAgICAgICAgICBmaWxlID0gZmlsZS5wYXRoKCJmaWx0ZXJlZCIsIHksICJnZW5lcy50c3YiKSwgCiAgICAgICAgICAgICAgICAgICAgIHNlcCA9ICJcdCIsIGNvbC5uYW1lcyA9IEZBTFNFLCByb3cubmFtZXMgPSBGQUxTRSwgcXVvdGUgPSBGQUxTRSkKICAgICAgIH0sCiAgICAgICBzY2UuZmlsdGVyZWQubGlzdCwKICAgICAgIG5hbWVzKHNjZS5maWx0ZXJlZC5saXN0KQopCgojIHJlbW92ZSB0aGVzZSBkYXRhIG5vdyAKcm0oc2NlLmxpc3QsIHNjZS5maWx0ZXJlZC5saXN0LCBlZC5vdXQsIGJyLCBici5maWx0ZXJlZCkKYGBgCgojIyByZWFkIGZpbHRlcmVkIG1hdHJpY2VzIGFuZCBwZXJmb3JtIFFDCnByaW9yIHRvIGJhdGNoLWNvcnJlY3Rpb24gYW5kIGludGVncmF0aW9uIEkgZG8gUUMgb24gZWFjaCBzYW1wbGUgc2VwZXJhdGVseS4KCnJlYWRpbmcgaW4gdGhlIHNwYXJzZSBtYXRyaWNlcyBvZiBmaWx0ZXJlZCBjZWxsczoKYGBge3IgY2FjaGU9VFJVRX0KcmVhZC5zYyA8LSBmdW5jdGlvbih4KSB7CiAgbXlkYXRhIDwtIHJlYWRNTShwYXN0ZTAoeCwgIi9tYXRyaXgubXR4IikpCiAgZmVhdHVyZXMgPC0gcmVhZC50YWJsZShwYXN0ZTAoeCwgIi9nZW5lcy50c3YiKSwgaGVhZGVyID0gRkFMU0UpCiAgYmFyY29kZXMgPC0gcmVhZC50YWJsZShwYXN0ZTAoeCwgIi9iYXJjb2Rlcy50c3YiKSwgaGVhZGVyID0gRkFMU0UpCiAgZGltbmFtZXMobXlkYXRhKSA8LSBsaXN0KGZlYXR1cmVzJFYyLCBiYXJjb2RlcyRWMSkgIyByb3duYW1lcyBhcmUgZ2VuZSBzeW1ib2xzIGFuZCBjb2xuYW1lcyBhcmUgYmFyY29kZXMKICBteWRhdGEKfQoKc2V0d2QoIn4vRG9jdW1lbnRzL0JlZ3VuL3Rtc18zX2FuZF8xOG1fZHJvcGxldF9raWRuZXkvZmlsdGVyZWQvIikgIyBmaWx0ZXJlZCBjZWxscyBkaXIKCnNhbXBsZXMgPC0gYygibW8zX01fOCIsICMgc2FtcGxlIG5hbWVzIGFyZSB1cGRhdGVkIHRvIHN0cmluZ3MgdGhhdCB3b3JrIHdpdGggUgogICAgICAgICAgICAgIm1vM19NXzkiLAogICAgICAgICAgICAgIm1vM19GXzU3IiwKICAgICAgICAgICAgICJtbzE4X0ZfNTAiLAogICAgICAgICAgICAgIm1vMThfRl81MSIsCiAgICAgICAgICAgICAibW8xOF9NXzUyIikKCmZpbHQubWF0cyA8LSBsYXBwbHkoc2FtcGxlcywgcmVhZC5zYykgIyByZWFkaW5nIGluIGZpbHRlcmVkIG1hdHJpY2VzIHRvIGEgbGlzdApuYW1lcyhmaWx0Lm1hdHMpIDwtIHNhbXBsZXMKYGBgCgppbml0aWF0ZSBzZXVyYXQgb2JqZWN0cyBmcm9tIGVhY2ggb2Ygc2l4IGdlbmUgWCBjZWxsIG1hdHJpY2VzOgpgYGB7ciBjYWNoZT1UUlVFfQpzby5saXN0IDwtIGxhcHBseShmaWx0Lm1hdHMsIGZ1bmN0aW9uKGkpIHsKICAgICAgICAgICAgICAgICAgICBDcmVhdGVTZXVyYXRPYmplY3QoY291bnRzID0gaSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1pbi5jZWxscyA9IDEwLCAjIGtlZXAgb25seSBnZW5lcyBleHByZXNzZWQgaW4gYXQgbGVhc3QgMTAgY2VsbHMKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJvamVjdCA9ICJraWRuZXkiKQogICAgICAgICAgICAgIH0KICAgICAgICApCgpybShmaWx0Lm1hdHMpICMgZG9uJ3QgbmVlZCB0aGUgbWF0cmljZXMgYW55bW9yZQpgYGAKKipRQyBiYXNlZCBvbiBtaXRvY2hvbmRyaWFsIGdlbmUgJSwgbnVtYmVyIG9mIFVNSXMsIG51bWJlciBvZiBmZWF0dXJlcy4qKiAKYGBge3J9CiMgbXRETkEKc28ubGlzdCA8LSBsYXBwbHkoc28ubGlzdCwgZnVuY3Rpb24oaSkgewogICAgICAgICAgICAgIGkkcGVyY2VudC5tdCA8LSBQZXJjZW50YWdlRmVhdHVyZVNldChpLCBwYXR0ZXJuID0gIl5tdC0iKQogICAgICAgICAgICAgIGkKICAgICAgICAgICAgfQogICAgICAgICAgKQpgYGAKCmBgYHtyIGZpZy5oZWlnaHQ9MjAsIGZpZy53aWR0aD02fQojIyBsb29rIGF0IGRpc3Qgb2YgbXRETkEsIG5VTUksIG5GZWF0dXJlcwpRQy52bG4ucGxvdHMgPC0gbGFwcGx5KHNvLmxpc3QsIGZ1bmN0aW9uKGRhdGEpIHsgCiAgICAgICAgICAgICAgICAgICAgICAgIFZsblBsb3QoZGF0YSwKICAgICAgICAgICAgICAgICAgICAgICAgZmVhdHVyZXMgPSBjKCJuRmVhdHVyZV9STkEiLCAibkNvdW50X1JOQSIsICJwZXJjZW50Lm10IikpICYgCiAgICAgICAgICAgICAgICAgICAgICAgIHRoZW1lKGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSwgYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCkpCiAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgKQpwbG90X2dyaWQocGxvdGxpc3QgPSBRQy52bG4ucGxvdHMsIG5jb2wgPSAxLCBsYWJlbHMgPSBzYW1wbGVzKQojUUMudmxuLnBsb3RzWzFdCiNRQy52bG4ucGxvdHNbMl0KI1FDLnZsbi5wbG90c1szXQojUUMudmxuLnBsb3RzWzRdCiNRQy52bG4ucGxvdHNbNV0KI1FDLnZsbi5wbG90c1s2XQpgYGAKCmBgYHtyfQojIHBsb3QgJSBtaXRvY2hvbmRyaWFsIGNvdW50cyB2cyB0b3RhbCBjb3VudHMgcGVyIGNlbGwKUUMubWl0b3NjYXR0ZXIucGxvdHMgPC0gbGFwcGx5KHNvLmxpc3QsIGZ1bmN0aW9uKGRhdGEpIHsgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgRmVhdHVyZVNjYXR0ZXIoZGF0YSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmZWF0dXJlMiA9ICJwZXJjZW50Lm10IiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmVhdHVyZTEgPSAibkNvdW50X1JOQSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHQuc2l6ZSA9IDAuNSwgY29scyA9ICJibGFjayIsKSAmIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRoZW1lX21pbmltYWwoYmFzZV9zaXplID0gOCkgJiAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJzKHRpdGxlID0gTlVMTCkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgKQoKcGxvdF9ncmlkKHBsb3RsaXN0ID0gUUMubWl0b3NjYXR0ZXIucGxvdHMsIGxhYmVscyA9IHNhbXBsZXMsIGxhYmVsX3NpemUgPSAxMCkKYGBgCgpzZWUgdGhlIGV4cGVjdGVkIHRyZW5kIG9mIGhpZ2ggbWl0byUgY2VsbHMgYWxzbyBoYXZpbmcgbG93ZXIgY291bnRzLCBidXQgdGhpcyAKaXMgbm90IHN0cmljdGx5IHRydWUuIE5leHQgc3RlcCBpcyB0byBhcHBseSBhIHJlYXNvbmFibGUgY3V0b2ZmLCBiYXNlZCBvbiB0aGVzZSBkaXN0cmlidXRpb25zIGJ1dCBhbHNvIGEgcHJpb3JpIGtub3dsZWRnZS4gdHlwaWNhbGx5IHdlIHdvdWxkIGNob29zZSB+MTAlIG1pdG9jaG9uZHJpYWwgY291bnRzLiBIb3dldmVyLCB0aGlzIGRhdGEgZG9lcyBoYXZlIGEgaGlnaCBwZXJjZW50YWdlIG9mIG1pdG9jaG9uZHJpYWwgY291bnRzIHNvIHRoYXQgd291bGQgcmVtb3ZlIG1hbnkgY2VsbHMuICMgYXBwbHkgc29tZSByZWFzb25hYmxlIHVwcGVyIGN1dG9mZnMgYmFzZWQgb24gdGhlc2UgZGlzdHJpYnV0aW9ucyBhbmQgcGxvdCBhZ2Fpbi4gQWxzbyBpdCBzZWVtcyB0aGUga2lkbmV5IGV4cHJlc3NlcwphIGxvdCBvZiBtdCBnZW5lcyBhbmQgZXhjbHVzaW9uIGNyaXRlcmlhIGlzIHNvbWV3aGF0IGNvbnRlc3RlZDogMzAgLSA1MCU6IGh0dHBzOi8vd3d3Lm5jYmkubmxtLm5paC5nb3YvcG1jL2FydGljbGVzL1BNQzY5NDAzODEvCgpiYXNlZCBvbiB0aGlzIDQwJSBzZWVtcyByZWFzb25hYmxlOgpgYGB7cn0Kc28ubGlzdC5taXRvZmlsdGVyIDwtIGxhcHBseShzby5saXN0LCBmdW5jdGlvbihpKSB7CiAgICAgICAgICAgICAgICAgICAgICBzdWJzZXQoaSwgcGVyY2VudC5tdCA8IDQwKSAgIyBzdWJzZXQgdG8gY2VsbHMgd2l0aCA8IDQwJSBtaXRvIGNvdW50cwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICkKIyB3aGF0IGZyYWN0aW9uIG9mIGJhcmNvZGVzIGRpZCB3ZSByZW1vdmU/Cm1hcHBseShmdW5jdGlvbihiZWZvcmUsIGFmdGVyKSB7IAogICAgICAgIChkaW0oYmVmb3JlKVsyXSAtIGRpbShhZnRlcilbMl0pIC8gZGltKGJlZm9yZSlbMl0gCiAgICAgIH0sCiAgICAgIHNvLmxpc3QsIHNvLmxpc3QubWl0b2ZpbHRlcgogICAgKQpgYGAKCioqZmlsdGVyIGJ5IFVNSXMgYW5kIGZlYXR1cmVzKioKbm90ZSB0aGF0IGVtcHR5ZHJvcHMgZmluZHMgYSBsb3Qgb2YgbG93IFVNSSBiYXJjb2RlcyBpdCB0aGlua3MgYXJlIGNlbGxzLApidXQgdGhleSBjb3VsZCBhbHNvIGJlIGFyZSBudWNsZWkgLyBjZWxsdWxhciBkZWJyaXMuIGZvciBhIGNvbnNlcnZhdGl2ZSBhbmFseXNpcyB1c2luZyBoaWdoIHF1YWxpdHkgY2VsbHMgd2l0aCBhIGhhcmQgY3V0b2ZmIG1heSBiZSBtb3JlIHBydWRlbnQKYGBge3J9CnNvLmxpc3QuY3V0b2ZmIDwtIGxhcHBseShzby5saXN0Lm1pdG9maWx0ZXIsIGZ1bmN0aW9uKGkpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICBzdWJzZXQoaSwgbkNvdW50X1JOQSA8IDMwMDAwICYgbkNvdW50X1JOQSA+IDEyMDApCiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICkKCiMgd2hhdCBmcmFjdGlvbiBvZiBiYXJjb2RlcyBkaWQgd2UgcmVtb3ZlPwptYXBwbHkoZnVuY3Rpb24oYmVmb3JlLCBhZnRlcikgeyAKICAgICAgICAgIChkaW0oYmVmb3JlKVsyXSAtIGRpbShhZnRlcilbMl0pIC8gZGltKGJlZm9yZSlbMl0gCiAgICAgICAgICB9LAogICAgICAgIHNvLmxpc3QubWl0b2ZpbHRlciwgc28ubGlzdC5jdXRvZmYKICAgICAgKQpgYGAKcGxvdCBRQyBkYXRhIGFnYWluOgpgYGB7ciBmaWcuaGVpZ2h0PTIwLCBmaWcud2lkdGg9Nn0KUUMudmxuLnBsb3RzIDwtIGxhcHBseShzby5saXN0LmN1dG9mZiwgZnVuY3Rpb24oZGF0YSkgeyAKICAgICAgICAgICAgICAgICAgICAgICAgVmxuUGxvdChkYXRhLAogICAgICAgICAgICAgICAgICAgICAgICBmZWF0dXJlcyA9IGMoIm5GZWF0dXJlX1JOQSIsICJuQ291bnRfUk5BIiwgInBlcmNlbnQubXQiKSkgJiAKICAgICAgICAgICAgICAgICAgICAgICAgdGhlbWUoYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpLCBheGlzLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSkKICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICApCnBsb3RfZ3JpZChwbG90bGlzdCA9IFFDLnZsbi5wbG90cywgbmNvbCA9IDEsIGxhYmVscyA9IHNhbXBsZXMpCmBgYAoKcmVtb3ZlIHVuZmlsdGVyZWQgZGF0YSBub3cKYGBge3J9CnJtKHNvLmxpc3QsIHNvLmxpc3QubWl0b2ZpbHRlcikKYGBgCgojIyBkYXRhIG5vcm1hbGl6YXRpb24gCgpub3JtYWxpemUgdGhlIGRhdGEgdXNpbmcgdGhlIFNDVHJhbnNmb3JtIG1ldGhvZC4gdGhpcyB1c2VzIGEgcmVndWxhcml6ZWQgCm5lZ2F0aXZlIGJpbm9taWFsIHJlZ3Jlc3Npb24gdG8gZXN0aW1hdGUgcmVndWxhcml6ZWQgcGFyYW1ldGVycyBmb3IgZGlmZmVyZW50Cmdyb3VwcyBvZiBnZW5lcy4gbWV0aG9kIG91dHBlcmZvcm1zIHVzaW5nIGEgc2luZ2xlIHNjYWxlIGZhY3RvciBmb3IgYWxsIGdlbmVzOiBodHRwczovL2dlbm9tZWJpb2xvZ3kuYmlvbWVkY2VudHJhbC5jb20vYXJ0aWNsZXMvMTAuMTE4Ni9zMTMwNTktMDE5LTE4NzQtMQoKYGBge3J9CnNvLmxpc3QuY3V0b2ZmIDwtIGxhcHBseShzby5saXN0LmN1dG9mZiwgZnVuY3Rpb24oaSkgeyAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgU0NUcmFuc2Zvcm0oaSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhcnMudG8ucmVncmVzcyA9ICJwZXJjZW50Lm10IiwgIyByZWdyZXNzaW5nIG5vcm1hbGl6YXRpb24gYWdhaW5zdCBtaXRvY2hvbmRyaWFsIGNvdW50IHBlcmNlbnQKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0aG9kID0gImdsbUdhbVBvaSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZlcmJvc2UgPSBGQUxTRSkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCmBgYAoKIyMgcHJlbGltaW5hcnkgZGltZW5zaW9uYWxpdHkgcmVkdWN0aW9uCgpkb2luZyBkaW0gcmVkdWN0aW9uIGFuZCBjbHVzdGVyaW5nIG9uIGVhY2ggc2FtcGxlIGJlZm9yZSBiYXRjaCBjb3JyZWN0aW9uLiBUSGlzIGlzIHRvIG1ha2Ugc3VyZSB0aGUgZGF0YSBsb29rcyBvayBhbmQgdGhpcyB3aWxsIGhlbHAgdmlzdWFsaXplIGRvdWJsZXRzIGxhdGVyCgpgYGB7ciBmaWcuZGltPWMoNyw1KX0KIyBydW4gUENBIGFuZCBkZWNpZGUgaG93IG1hbnkgUENzIHRvIHVzZSBmb3IgY2x1c3RlcmluZyBhbmQgVU1BUApzby5saXN0LmN1dG9mZiA8LSBsYXBwbHkoc28ubGlzdC5jdXRvZmYsCiAgICAgICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbihpKSB7IGkgJT4lIFJ1blBDQSgpIH0pCgplbGJvd3Bsb3RzIDwtIGxhcHBseShzby5saXN0LmN1dG9mZiwgZnVuY3Rpb24oaSkgeyAKICAgICAgICAgICAgICAgICAgICAgICAgICBFbGJvd1Bsb3QoaSwgbmRpbXMgPSA0MCwgcmVkdWN0aW9uID0gInBjYSIpICsgCiAgICAgICAgICAgICAgICAgICAgICAgICAgdGhlbWVfbWluaW1hbChiYXNlX3NpemUgPSA4KQogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICApCnBsb3RfZ3JpZChwbG90bGlzdCA9IGVsYm93cGxvdHMsIGxhYmVscyA9IHNhbXBsZXMsIGxhYmVsX3NpemUgPSA4KQpgYGAKCmV4cGxhaW5lZCB2YXJpYW5jZSBsb29rcyB2ZXJ5IHNtYWxsIGFmdGVyIFBDIDI2CmBgYHtyfQpQQ3MgPC0gMToyNgpgYGAKCmNvbnRpbnVlIHdpdGggU05OIGdyYXBoIGNvbnN0cnVjdGlvbiwgVU1BUCwgTGVpZGVuIGNsdXN0ZXJpbmcKYGBge3IgcmVzdWx0cz0naGlkZSd9CiNyZXRpY3VsYXRlOjp2aXJ0dWFsZW52X2NyZWF0ZSgpICMgdG8gdXNlIGxlaWRlbiBhbGc6IG11c3QgZ28gdGhyb3VnaCByZXRpY3VsYXRlIHRvIHVzZSBweXRob24gaW4gUgoKI3B5X2luc3RhbGwoImxlaWRlbmFsZyIpCgpzby5saXN0LmN1dG9mZiA8LSBsYXBwbHkoc28ubGlzdC5jdXRvZmYsCiAgICAgICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbihpKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgIGkgJT4lIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIEZpbmROZWlnaGJvcnMoZGltcyA9IFBDcykgJT4lICAjIG1ha2UgU05OIGdyYXBoCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUnVuVU1BUChkaW1zID0gUENzKSAlPiUgICAgICAgICMgcGVyZm9ybSBVTUFQCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgRmluZENsdXN0ZXJzKGFsZ29yaXRobSA9ICJsZWlkZW4iKSAjIGRvIGNsdXN0ZXJpbmcgd2l0aCBMZWlkZW4gYWxnb3JpdGhtCiAgICAgICAgICAgICAgICAgICAgICAgICB9KQpgYGAKCmNoZWNrIHRoZSBtb3N0IHZhcmlhYmxlIGZlYXR1cmVzIGFtb25nIHNhbXBsZXMgZm9yIGdlbmVyYWwgb3ZlcmxhcDoKYGBge3J9CnRvcDIwIDwtIGxhcHBseShzby5saXN0LmN1dG9mZiwgZnVuY3Rpb24oaSkgeyBoZWFkKFZhcmlhYmxlRmVhdHVyZXMoaSksIDIwKSB9KSAjIHRvcCAyMCB2YXJpYWJsZSBmZWF0dXJlcwprbml0cjo6a2FibGUoYXMuZGF0YS5mcmFtZShkby5jYWxsKGNiaW5kLCB0b3AyMCkpLCBmb3JtYXQgPSAibWFya2Rvd24iKQpgYGAKCnBsb3QgdmFyaWFuY2UgYWdhaW5zdCBhdmVyYWdlIGdlbmUgZXhwcmVzc2lvbjoKYGBge3IgZmlnLndpZHRoPTcsIGZpZy5oZWlnaHQ9MTV9ClZGLnBsb3RzIDwtIGxhcHBseShzby5saXN0LmN1dG9mZiwgZnVuY3Rpb24oaSkgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFZhcmlhYmxlRmVhdHVyZVBsb3QoaSwgcHQuc2l6ZSA9IDAuNSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZWxlY3Rpb24ubWV0aG9kID0gInNjdCIpICsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aGVtZV9taW5pbWFsKGJhc2Vfc2l6ZSA9IDgpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQoKb3B0aW9ucyhnZ3JlcGVsLm1heC5vdmVybGFwcyA9IEluZikKClZGLnBsb3RzLmxhYnMgPC0gbWFwcGx5KGZ1bmN0aW9uKHZmLCBsYWJlbHMpIHsKICAgICAgICAgICAgICAgICAgICAgICAgTGFiZWxQb2ludHMocGxvdCA9IHZmLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcG9pbnRzID0gbGFiZWxzLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVwZWwgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB4bnVkZ2UgPSAwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5bnVkZ2UgPSAwKQogICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgIFZGLnBsb3RzLCB0b3AyMCwgU0lNUExJRlkgPSBGQUxTRQogICAgICAgICAgICAgICAgICApCgpwbG90X2dyaWQocGxvdGxpc3QgPSBWRi5wbG90cy5sYWJzLCBuY29sID0gMSwgbGFiZWxzID0gc2FtcGxlcykKYGBgCgojIyBwcmVsaW1pbmFyeSBkaW0gcmVkdWN0aW9uIC8gY2x1c3RlcmluZyB2aXN1YWxpemF0aW9uCgp0aGlzIGlzIHRvIG1ha2Ugc3VyZSB0aGUgZGF0YSBkb2Vzbid0IGxvb2sgb3V0IG9mIHRoZSBvcmRpbmFyeSBvbiBwZXItc2FtcGxlIGJhc2lzLiB3ZSB3aWxsIHVzZSB0aGVzZSBjbHVzdGVycyB0byBkbyBkb3VibGV0IGRldGVjdGlvbi4KYGBge3J9CiMgdGhpcyBmdW5jdGlvbiBhZGp1c3RzIGFscGhhICh0cmFuc3BhcmVuY3kpIG9mIHBvaW50cyBpbiBnZ3Bsb3Qgb2JqZWN0cwphZGp1c3QuYWxwaGEgPC0gZnVuY3Rpb24ocGxvdCwgYSkgeyAKICAgICAgICAgICAgICAgICAgICBwbG90W1sxXV0kbGF5ZXJzW1sxXV0kYWVzX3BhcmFtcyRhbHBoYSA8LSBhIAogICAgICAgICAgICAgICAgICAgIHBsb3QKICAgICAgICAgICAgICAgICAgIH0KYGBgCgpgYGB7ciBmaWcud2lkdGg9OC41LCBmaWcuaGVpZ2h0PTUuNX0KIyBQQzEgdnMgUEMyClBDQS4xMi5wbG90cyA8LSBsYXBwbHkoc28ubGlzdC5jdXRvZmYsIGZ1bmN0aW9uKGkpIHsKICAgICAgICAgICAgICAgICAgICAgICBEaW1QbG90KG9iamVjdCA9IGksIGRpbXMgPSBjKDEsMiksIHJlZHVjdGlvbiA9ICJwY2EiKSArCiAgICAgICAgICAgICAgICAgICAgICAgdGhlbWVfbWluaW1hbChiYXNlX3NpemUgPSA4KQogICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICkKClBDQS4xMi5wbG90cyA8LSBsYXBwbHkoUENBLjEyLnBsb3RzLCBhZGp1c3QuYWxwaGEsIGEgPSAwLjcpCnBsb3RfZ3JpZChwbG90bGlzdCA9IFBDQS4xMi5wbG90cywgbGFiZWxzID0gc2FtcGxlcykKYGBgCgpgYGB7ciBmaWcud2lkdGg9OC41LCBmaWcuaGVpZ2h0PTUuNX0KIyBQQzEgdnMgUEMzClBDQS4xMy5wbG90cyA8LSBsYXBwbHkoc28ubGlzdC5jdXRvZmYsIGZ1bmN0aW9uKGkpIHsKICAgICAgICAgICAgICAgICAgICAgICBEaW1QbG90KG9iamVjdCA9IGksIGRpbXMgPSBjKDEsMyksIHJlZHVjdGlvbiA9ICJwY2EiKSArCiAgICAgICAgICAgICAgICAgICAgICAgdGhlbWVfbWluaW1hbChiYXNlX3NpemUgPSA4KQogICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICkKClBDQS4xMy5wbG90cyA8LSBsYXBwbHkoUENBLjEzLnBsb3RzLCBhZGp1c3QuYWxwaGEsIGEgPSAwLjcpCnBsb3RfZ3JpZChwbG90bGlzdCA9IFBDQS4xMy5wbG90cywgbGFiZWxzID0gc2FtcGxlcykKYGBgCgpgYGB7ciBmaWcud2lkdGg9OC41LCBmaWcuaGVpZ2h0PTUuNX0KIyBVTUFQCnVtYXAucGxvdHMgPC0gbGFwcGx5KHNvLmxpc3QuY3V0b2ZmLCBmdW5jdGlvbihpKSB7CiAgICAgICAgICAgICAgICAgICAgIERpbVBsb3Qob2JqZWN0ID0gaSwgcmVkdWN0aW9uID0gInVtYXAiLCBwdC5zaXplID0gMC43NSkgKwogICAgICAgICAgICAgICAgICAgICB0aGVtZV9taW5pbWFsKGJhc2Vfc2l6ZSA9IDgpCiAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgKQoKdW1hcC5wbG90cyA8LSBsYXBwbHkodW1hcC5wbG90cywgYWRqdXN0LmFscGhhLCBhID0gMC43KQpwbG90X2dyaWQocGxvdGxpc3QgPSB1bWFwLnBsb3RzLCBsYWJlbHMgPSBzYW1wbGVzKQpgYGAKClNhbml0eSBjaGVjayB3aXRoIHNvbWUgbWFya2VyIGdlbmVzIGZyb20gVGFidWxhIE11cmlzCgoqKkFjdGEyKiogKG1lc2FuZ2lhbCBjZWxscyk6CmBgYHtyIGZpZy53aWR0aD04LjUsIGZpZy5oZWlnaHQ9NS41fQpBY3RhMi5wbG90cyA8LSBsYXBwbHkoc28ubGlzdC5jdXRvZmYsIGZ1bmN0aW9uKGkpIHsKICAgICAgICAgICAgICAgICAgICAgRmVhdHVyZVBsb3Qob2JqZWN0ID0gaSwgZmVhdHVyZXMgPSAiQWN0YTIiLCByZWR1Y3Rpb24gPSAidW1hcCIpICsKICAgICAgICAgICAgICAgICAgICAgdGhlbWVfbWluaW1hbChiYXNlX3NpemUgPSA4KSArCiAgICAgICAgICAgICAgICAgICAgIHRoZW1lKHRpdGxlID0gZWxlbWVudF9ibGFuaygpKQogICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICkKcGxvdF9ncmlkKHBsb3RsaXN0ID0gQWN0YTIucGxvdHMsIGxhYmVscyA9IHNhbXBsZXMpCmBgYAoKKipFbWNuKiogKGNhcGlsbGFyeSBlbmRvdGhlbGl1bSk6CmBgYHtyIGZpZy53aWR0aD04LjUsIGZpZy5oZWlnaHQ9NS41fQpFbWNuLnBsb3RzIDwtIGxhcHBseShzby5saXN0LmN1dG9mZiwgZnVuY3Rpb24oaSkgewogIEZlYXR1cmVQbG90KG9iamVjdCA9IGksIGZlYXR1cmVzID0gIkVtY24iLCByZWR1Y3Rpb24gPSAidW1hcCIpICsKICAgIHRoZW1lX21pbmltYWwoYmFzZV9zaXplID0gOCkgKwogICAgdGhlbWUodGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpCn0KKQpwbG90X2dyaWQocGxvdGxpc3QgPSBFbWNuLnBsb3RzLCBsYWJlbHMgPSBzYW1wbGVzKQpgYGAKCioqRW1yMSoqIChtYWNyb3BoYWdlKToKYGBge3IgZmlnLndpZHRoPTguNSwgZmlnLmhlaWdodD01LjV9CkVtcjEucGxvdHMgPC0gbGFwcGx5KHNvLmxpc3QuY3V0b2ZmLCBmdW5jdGlvbihpKSB7CiAgRmVhdHVyZVBsb3Qob2JqZWN0ID0gaSwgZmVhdHVyZXMgPSAiQWRncmUxIiwgcmVkdWN0aW9uID0gInVtYXAiKSArCiAgICB0aGVtZV9taW5pbWFsKGJhc2Vfc2l6ZSA9IDgpICsKICAgIHRoZW1lKHRpdGxlID0gZWxlbWVudF9ibGFuaygpKQp9CikKcGxvdF9ncmlkKHBsb3RsaXN0ID0gRW1yMS5wbG90cywgbGFiZWxzID0gc2FtcGxlcykKYGBgCgoKIyMgRG91YmxldCBwcmVkaWN0aW9uCgpJJ20gdXNpbmcgRG91YmxldEZpbmRlciB3aGljaCBpcyBjb21wYXJlcyByZWFsIGNlbGxzIHRvIHNpbXVsYXRlZCBkb3VibGV0IHRyYW5zY3JpcHRvbWVzLiBIZXJlIEkgYW0gaWdub3JpbmcgaG9tb3R5cGljIGRvdWJsZXRzLSBmb3IgdGhpcyBiYXNpYyBvdmVydmlldyBpdCBpcyBwcm9iYWJseSBub3QgZ29pbmcgdG8gaW1wYWN0IHJlc3VsdHMuCgpJbiBhIHJlYWwtd29ybGQgYXBwbGljYXRpb24gd2l0aCBtb3JlIHRpbWUgSSB3b3VsZCB0cnkgdG8gZXN0aW1hdGUgaG9tb3R5cGljIGRvdWJsZXQKcmF0ZXMgZnJvbSBncm91bmQtdHJ1dGggY2VsbC10eXBlIHByb3BvcnRpb25zIHRvIElEIHRob3NlIGNhbmRpZGF0ZXMgYXMgd2VsbCAKCmVzdGltYXRlIG9mIGRvdWJsZXQgcmF0ZSBmcm9tIFRhYnVsYSBNdXJpcyBwYXBlci0gCiJDZWxscyB3ZXJlIGxvYWRlZCBpbiBlYWNoIGNoYW5uZWwgd2l0aCBhIHRhcmdldCBvdXRwdXQgb2YgNSwwMDAgY2VsbHMgcGVyIHNhbXBsZSIKYXQgNSwwMDAgY2VsbHMgZXhwZWN0ZWQgZG91YmxldCByYXRlIGlzIH4yLjQlCgpwZXJmb3JtIHBhcmFtZXRlciBzd2VlcGluZyBvZiBwSyAobmVpZ2hib3Job29kIHNpemUpIHdpdGggcmFuZ2Ugb2YgdmFscyBmb3IgcE4gKG51bWJlciBhcnRpZmljaWFsIGRvdWJsZXRzKQpgYGB7ciByZXR1cm49RkFMU0V9CnN3ZWVwLnJlcy5saXN0IDwtIGxhcHBseShzby5saXN0LmN1dG9mZiwgIyBwYXJhbWV0ZXIgc3dlZXBpbmcgbmVpZ2hib3Job29kIHNpemUKICAgICAgICAgICAgICAgICAgICAgICAgIGZ1bmN0aW9uKGkpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFyYW1Td2VlcF92MyhpLCBQQ3MgPSBQQ3MsIHNjdCA9IFRSVUUpCiAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICkKCnN3ZWVwLnN0YXRzIDwtIGxhcHBseShzd2VlcC5yZXMubGlzdCwgICMgZ2V0IHN1bW1hcnkgb2YgcGFyYW1ldGVyIHN3ZWVwcy4gUk9DIGFuYWx5c2lzCiAgICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbihpKSB7CiAgICAgICAgICAgICAgICAgICAgICAgIHN1bW1hcml6ZVN3ZWVwKGksIEdUID0gRkFMU0UpCiAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgKQpiY212biA8LSBsYXBwbHkoc3dlZXAuc3RhdHMsIGZpbmQucEspICMgZ2V0IEFVQyBpbiBST0MgYW5hbHlzaXMgZm9yIGVhY2ggcEsKCnBLbWF4IDwtIGxhcHBseShiY212biwgZnVuY3Rpb24oaSkgeyBpW3doaWNoLm1heChpJEJDbWV0cmljKSxdJHBLIH0gKSAjIGdldCB0aGUgbWF4IHBLCnBLbWF4IDwtIGFzX3ZlY3Rvcih0eXBlLmNvbnZlcnQocEttYXgsIGFzLmlzID0gVFJVRSkpCmBgYAoKbG9va2luZyBhdCBvdXIgcEsgdmFsdWVzIHRoYXQgbWF4aW1pemUgQVVDCmBgYHtyfQpwcmludChwS21heCkKYGBgCgoKZXhwZWN0aW5nIDIuNCUgZG91YmxldHMgYmFzZWQgb24gMTBYIGd1aWRlIGFuZCA1MDAwIGNlbGxzIGxvYWRlZApgYGB7cn0KbkV4cCA8LSBsYXBwbHkoc28ubGlzdC5jdXRvZmYsIGZ1bmN0aW9uKGkpIHsgcm91bmQobmNvbChpKSAqIDAuMDI0KSB9KQpgYGAKCnJ1biBkb3VibGV0ZmluZGVyOgpgYGB7ciByZXR1cm4gPSBGQUxTRX0KIyBmb3IgZWFjaCBzYW1wbGUsIHVzaW5nIGluZGl2aWR1YWwgcEttYXggYW5kIGNvbnN0YW50IGRvdWJsZXQgcmF0ZQpzby5maWx0ZXJlZCA8LSBtYXBwbHkoZnVuY3Rpb24ob2JqLCBwS20sIGV4cF9kb3ViKSB7CiAgICAgICAgICAgICAgICAgICAgICAgIGRvdWJsZXRGaW5kZXJfdjMoc2V1ID0gb2JqLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwTiA9IDAuMjUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBLID0gcEttLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuRXhwID0gYXMubnVtZXJpYyhleHBfZG91YiksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFBDcyA9IFBDcywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzY3QgPSBUUlVFKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAgICBzby5saXN0LmN1dG9mZiwgcEttYXgsIG5FeHAKICAgICAgICAgICAgICAgICAgICAgICkKYGBgCgp2aXN1YWxpemUgZG91YmxldCBwcm9wZXJ0aWVzLiBXZSBleHBlY3QgZG91YmxldHMgdG8gaGF2ZSBhIGhpZ2hlciBudW1iZXIgb2YgZmVhdHVyZXMgdGhhbiBhdmVyYWdlLgoKYGBge3IgZmlnLndpZHRoPTUsIGZpZy5oZWlnaHQ9MjB9CiMgbmFtZSBvZiB0aGUgREYgcHJlZGljdGlvbiBpcyBkaWZmZXJlbnQgZGVwZW5kaW5nIG9uIHBhcmFtZXRlcnMsIHNvIGV4dHJhY3QgdGhlIGNvcnJlY3QgY29sdW1uIG5hbWUgZm9yIGVhY2ggc2FtcGxlLgpERi5uYW1lID0gbGFwcGx5KHNvLmZpbHRlcmVkLCBmdW5jdGlvbihpKSB7CiAgICAgIGNvbG5hbWVzKGlAbWV0YS5kYXRhKVtncmVwbCgiREYuY2xhc3NpZmljYXRpb24iLCBjb2xuYW1lcyhpQG1ldGEuZGF0YSkpXSAKICAgICAgICAgICAgICAgICAgICAgICAgfQogICkKCmRibHQudmxuIDwtIG1hcHBseShmdW5jdGlvbihvYmosIHByZWRpY3Rpb24pIHsKICAgICAgICAgICAgICAgICAgICBWbG5QbG90KG9iaiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBmZWF0dXJlcyA9IGMoIm5GZWF0dXJlX1JOQSIsICJuQ291bnRfUk5BIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBncm91cC5ieSA9IHByZWRpY3Rpb24sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgcHQuc2l6ZSA9IDAuMikKICAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICBzby5maWx0ZXJlZCwgREYubmFtZSwKICAgICAgICAgICAgICAgICAgIFNJTVBMSUZZID0gRkFMU0UKICAgICAgICAgICAgICAgICAgICkKcGxvdF9ncmlkKHBsb3RsaXN0ID0gZGJsdC52bG4sIG5jb2wgPSAxLCBsYWJlbHMgPSBzYW1wbGVzLCBsYWJlbF94ID0gLS4wNSkKYGBgCgpgYGB7ciBmaWcud2lkdGg9OCwgZmlnLmhlaWdodD0xMX0KZGJsdC51bWFwIDwtIG1hcHBseShmdW5jdGlvbihvYmosIHByZWRpY3Rpb24pIHsKICAgICAgICAgICAgICAgICAgICAgICAgRGltUGxvdChvYmosCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXAuYnkgPSBwcmVkaWN0aW9uLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwdC5zaXplID0gMC40LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWR1Y3Rpb24gPSAidW1hcCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb3JkZXIgPSAiRG91YmxldCIpICsgCiAgICAgICAgICAgICAgICAgICAgICAgIGxhYnModGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpCiAgICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAgc28uZmlsdGVyZWQsIERGLm5hbWUsCiAgICAgICAgICAgICAgICAgICAgICBTSU1QTElGWSA9IEZBTFNFCiAgICAgICAgICAgICAgICAgICAgICApCgpwbG90X2dyaWQocGxvdGxpc3QgPSBkYmx0LnVtYXAsIG5jb2wgPSAyLCBsYWJlbHM9IHNhbXBsZXMpCmBgYAoKbm90IHNlZWluZyBhIGNsZWFyIHRyZW5kIG9mIGdyZWF0ZXIgY291bnRzIGFuZCBmZWF0dXJlcyBpbiBkb3VibGV0cy4gdGhpcyBpcyBub3QgcmVxdWlyZWQgb2YgYSBkb3VibGV0IGJ1dCBleHBlY3Qgc2VlIGEgdHJlbmQuIEEgYmlvbG9naWNhbCBleHBsYW5hdGlvbiB3b3VsZCBiZSB0aGF0IHNvbWUgY2VsbCB0eXBlcyBjb3VsZCBleHByZXNzIG1hbnkgbW9yZSBmZWF0dXJlcyB0aGFuIG90aGVycy4gV2l0aCBtb3JlIHRpbWUgdGhpcyB3b3VsZCBiZSBhbiBhcmVhIHRoYXQgSSB3b3VsZCB3YW50IHRvIHRyb3VibGVzaG9vdAoKcmVtb3ZlIGRvdWJsZXRzOgpgYGB7cn0Kc28ubm9kYiA8LSBtYXBwbHkoZnVuY3Rpb24ob2JqLCBwcmVkaWN0aW9uKSB7CiAgICAgICAgICAgICAgb2JqWywgb2JqQG1ldGEuZGF0YVssIHByZWRpY3Rpb25dID09ICJTaW5nbGV0Il0gIyBzZWxlY3QgYmFyY29kZXMgcHJlZGljdGVkIHRvIGJlIHNpbmdsZXRzCiAgICAgICAgICAgICB9LAogICAgICAgICAgICBzby5maWx0ZXJlZCwgREYubmFtZSwgCiAgICAgICAgICAgIFNJTVBMSUZZID0gRkFMU0UKKQpgYGAKCiMjIGJhdGNoIGNvcnJlY3Rpb24gYW5kIGRhdGEgaW50ZWdyYXRpb24KCmJhdGNoIGNvcnJlY3Rpb24gdXNpbmcgQ29tcHJlaGVuc2l2ZSBJbnRlZ3JhdGlvbiBBbGdvcml0aG0gKFN0dWFydCBldCBhbCAyMDE5KSwgY2Fub25pY2FsIGNvcnJlbGF0aW9uIGFuYWx5c2lzIGlzIHVzZWQgdG8gY2FwdHVyZSBmZWF0dXJlcyB0aGF0IGNvcnJlbGF0ZSBhbW9uZyBiYXRjaGVzLCB0aGVuIG11dHVhbCBuZWFyZXN0IG5laWdoYm9ycyBhcmUgZm91bmQgdG8gbWFrZSBhIGdyYXBoIG9mIGFsbCBzYW1wbGVzCgpwcmVwYXJlIHRoZSBkYXRhOgpgYGB7cn0KIyBhZGQgbmFtZXMgdG8gZWFjaCBzYW1wbGUgZGF0YXNldApzby5ub2RiIDwtIG1hcHBseShmdW5jdGlvbih4LCB5KSB7CiAgICAgICAgICAgICAgICAgICAgICAgICBBZGRNZXRhRGF0YShvYmplY3QgPSB4LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0YWRhdGEgPSB5LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sLm5hbWUgPSAic2FtcGxlIikKICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICBzby5ub2RiLCBzYW1wbGVzLAogICAgICAgICAgICAgICAgICAgICAgU0lNUExJRlkgPSBGQUxTRQogICAgICAgICAgICAgICAgICAgICkKCiMgYWRkIGFnZSBhbmQgc2V4IGluZm9ybWF0aW9uCnNvLm5vZGIkbW8zX01fOCAgICU8PiUgQWRkTWV0YURhdGEobWV0YWRhdGEgPSAiMyIsICBjb2wubmFtZSA9ICJhZ2UiKQpzby5ub2RiJG1vM19NXzkgICAlPD4lIEFkZE1ldGFEYXRhKG1ldGFkYXRhID0gIjMiLCAgY29sLm5hbWUgPSAiYWdlIikKc28ubm9kYiRtbzNfRl81NyAgJTw+JSBBZGRNZXRhRGF0YShtZXRhZGF0YSA9ICIzIiwgIGNvbC5uYW1lID0gImFnZSIpCnNvLm5vZGIkbW8xOF9GXzUwICU8PiUgQWRkTWV0YURhdGEobWV0YWRhdGEgPSAiMTgiLCBjb2wubmFtZSA9ICJhZ2UiKQpzby5ub2RiJG1vMThfRl81MSAlPD4lIEFkZE1ldGFEYXRhKG1ldGFkYXRhID0gIjE4IiwgY29sLm5hbWUgPSAiYWdlIikKc28ubm9kYiRtbzE4X01fNTIgJTw+JSBBZGRNZXRhRGF0YShtZXRhZGF0YSA9ICIxOCIsIGNvbC5uYW1lID0gImFnZSIpCnNvLm5vZGIkbW8zX01fOCAgICU8PiUgQWRkTWV0YURhdGEobWV0YWRhdGEgPSAiTSIsICBjb2wubmFtZSA9ICJzZXgiKQpzby5ub2RiJG1vM19NXzkgICAlPD4lIEFkZE1ldGFEYXRhKG1ldGFkYXRhID0gIk0iLCAgY29sLm5hbWUgPSAic2V4IikKc28ubm9kYiRtbzNfRl81NyAgJTw+JSBBZGRNZXRhRGF0YShtZXRhZGF0YSA9ICJGIiwgIGNvbC5uYW1lID0gInNleCIpCnNvLm5vZGIkbW8xOF9GXzUwICU8PiUgQWRkTWV0YURhdGEobWV0YWRhdGEgPSAiRiIsICBjb2wubmFtZSA9ICJzZXgiKQpzby5ub2RiJG1vMThfRl81MSAlPD4lIEFkZE1ldGFEYXRhKG1ldGFkYXRhID0gIkYiLCAgY29sLm5hbWUgPSAic2V4IikKc28ubm9kYiRtbzE4X01fNTIgJTw+JSBBZGRNZXRhRGF0YShtZXRhZGF0YSA9ICJNIiwgIGNvbC5uYW1lID0gInNleCIpCmBgYAoKaWRlbnRpZnkgdGhlIHNoYXJlZCB2YXJpYWJsZSBmZWF0dXJlcyBhbW9uZyBkYXRhc2V0cyBhbmQgc3BlY2lmaWNhbGx5IHByZXBhcmUgdG8gdXNlIGludGVncmF0aW9uIHdpdGggU0NUcmFuc2Zvcm0gbWV0aG9kCmBgYHtyfQpzaGFyZWQudmFyLmZlYXR1cmVzIDwtIFNlbGVjdEludGVncmF0aW9uRmVhdHVyZXMob2JqZWN0Lmxpc3QgPSBzby5ub2RiLCBuZmVhdHVyZXMgPSAzMDAwKQpzby5ub2RiICU8PiUgUHJlcFNDVEludGVncmF0aW9uKGFuY2hvci5mZWF0dXJlcyA9IHNoYXJlZC52YXIuZmVhdHVyZXMsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZlcmJvc2UgPSBGQUxTRSkKYGBgCgppZGVudGlmeSBpbnRlZ3JhdGlvbiBhbmNob3JzIGFuZCBpbnRlZ3JhdGUgdGhlIGRhdGEKYGBge3IgcmV0dXJuPUZBTFNFfQphbmNob3JzIDwtIEZpbmRJbnRlZ3JhdGlvbkFuY2hvcnMob2JqZWN0Lmxpc3QgPSBzby5ub2RiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5vcm1hbGl6YXRpb24ubWV0aG9kID0gIlNDVCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYW5jaG9yLmZlYXR1cmVzID0gc2hhcmVkLnZhci5mZWF0dXJlcywgdmVyYm9zZSA9IEZBTFNFKQpraWRuZXkuaW50ZWdyYXRlZCA8LSBJbnRlZ3JhdGVEYXRhKGFuY2hvcnNldCA9IGFuY2hvcnMsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5vcm1hbGl6YXRpb24ubWV0aG9kID0gIlNDVCIsIHZlcmJvc2UgPSBGQUxTRSkKYGBgCgpyZW1vdmUgc29tZSBiaWcgb2JqZWN0cyB3ZSBubyBsb25nZXIgbmVlZApgYGB7ciByZXR1cm49RkFMU0V9CnJtKGFuY2hvcnMsIHNvLmxpc3QuY3V0b2ZmLCBzby5maWx0ZXJlZCwgc28ubGlzdC5taXRvZmlsdGVyLCBzby5saXN0LCBzd2VlcC5yZXMubGlzdCkKYGBgCgpub3cgYW5hbHl6ZSBjb21iaW5lZCBkYXRhc2V0CmBgYHtyfQpEZWZhdWx0QXNzYXkoa2lkbmV5LmludGVncmF0ZWQpIDwtICJpbnRlZ3JhdGVkIiAjIHRoaXMganVzdCBzZXRzIHRoZSBkZWZhdWx0IHNsb3QgaW4gdGhlIHNldXJhdCBvYmplY3QgdG8gYmUgdGhlIGludGVncmF0ZWQgZGF0YSByYXRoZXIgdGhhbiB1bi1jb3JyZWN0ZWQgZGF0YQpgYGAKCiMjIGRpbWVuc2lvbmFsaXR5IHJlZHVjdGlvbiBhbmQgY2x1c3RlcmluZyBvZiBpbnRlZ3JhdGVkIGRhdGEKdGhlIGNvdW50cyBkYXRhIGlzIGFscmVhZHkgbm9ybWFsaXplZCBhbmQgYmF0Y2gtY29ycmVjdGVkIGZyb20gdGhlIHVwc3RyZWFtCnByb2Nlc3NpbmcuIGhvd2V2ZXIgbm93IHRoYXQgd2UgaGF2ZSBhbGwgc2FtcGxlcyBjb21iaW5lIHdlIHdpbGwgbmVlZCB0byByZS1ydW4gUENBLCAKbWFrZSBhIG5ldyBTTk4gZ3JhcGgsIGZpbmQgY2x1c3RlcnMgYW5kIHBlcmZvcm0gVU1BUCBhZ2FpbgoKUENBOgpgYGB7cn0Ka2lkbmV5LmludGVncmF0ZWQgJTw+JSBSdW5QQ0EoKQpraWRuZXkuaW50ZWdyYXRlZCAlPiUgRWxib3dQbG90KG5kaW1zID0gNDAsIHJlZHVjdGlvbiA9ICJwY2EiKQpgYGAKClBDMjcgbG9va3MgbGlrZSBhIGdvb2QgYnJlYWsgcG9pbnQKYGBge3J9ClBDcyA9IDE6MjcKYGBgCgpwbG90IFBDQToKYGBge3IgZmlnLndpZHRoID0gNywgZmlnLmhlaWdodD0gMy4zfQpQQ0EuMTIucGxvdCA8LSBEaW1QbG90KGtpZG5leS5pbnRlZ3JhdGVkLCAjIFBDMSB2cyBQQzIKICAgICAgICAgICAgICAgICAgICAgICBkaW1zID0gYygxLDIpLCAKICAgICAgICAgICAgICAgICAgICAgICByZWR1Y3Rpb24gPSAicGNhIiwgCiAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXAuYnkgPSAiYWdlIiwgCiAgICAgICAgICAgICAgICAgICAgICAgc2h1ZmZsZSA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgcHQuc2l6ZSA9IDAuMykgKwogICAgICAgICAgICAgICAgICAgICAgIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTEpKSArCiAgICAgICAgICAgICAgICAgICAgICAgbGFicyhjb2xvciA9ICJhZ2UgKG1vKSIpICMlPiUgYWRqdXN0LmFscGhhKGEgPSAwLjgpClBDQS4xMy5wbG90IDwtIERpbVBsb3Qoa2lkbmV5LmludGVncmF0ZWQsCiAgICAgICAgICAgICAgICAgICAgICAgZGltcyA9IGMoMSwzKSwgCiAgICAgICAgICAgICAgICAgICAgICAgcmVkdWN0aW9uID0gInBjYSIsIAogICAgICAgICAgICAgICAgICAgICAgIGdyb3VwLmJ5ID0gImFnZSIsCiAgICAgICAgICAgICAgICAgICAgICAgc2h1ZmZsZSA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgcHQuc2l6ZSA9IDAuMykgKwogICAgICAgICAgICAgICAgICAgICAgIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTEpKSsKICAgICAgICAgICAgICAgICAgICAgICBsYWJzKGNvbG9yID0gImFnZSAobW8pIikjJT4lIGFkanVzdC5hbHBoYShhID0gMC44KQpQQ0EuMTIucGxvdCAlPD4lIGFkanVzdC5hbHBoYShhID0gMC44KQpQQ0EuMTMucGxvdCAlPD4lIGFkanVzdC5hbHBoYShhID0gMC44KQpwbG90X2dyaWQoUENBLjEyLnBsb3QsIFBDQS4xMy5wbG90KQpgYGAKCmNyZWF0ZSBzaGFyZWQgbmVhcmVzdCBuZWlnaGJvciBncmFwaDoKYGBge3J9CmtpZG5leS5pbnRlZ3JhdGVkICU8PiUgRmluZE5laWdoYm9ycyhyZWR1Y3Rpb24gPSAicGNhIiwgZGltcyA9IFBDcykKYGBgCgpkbyBMZWlkZW4gY2x1c3RlcmluZyBhdCBhIHNlcmllcyBvZiByZXNvbHV0aW9uczoKYGBge3J9CmtpZG5leS5pbnRlZ3JhdGVkICU8PiUgRmluZENsdXN0ZXJzKAogICAgICAgICAgICAgICAgICAgICAgICAgIGFsZ29yaXRobSA9ICJsZWlkZW4iLAogICAgICAgICAgICAgICAgICAgICAgICAgIHJlc29sdXRpb24gPSBzZXEoMC4yLDEuNCwwLjEpLAogICAgICAgICAgICAgICAgICAgICAgICAgIHZlcmJvc2UgPSBGQUxTRSkKcHJpbnQoc2FwcGx5KAogICAgICAgIGdyZXAoInJlcyIsIGNvbG5hbWVzKGtpZG5leS5pbnRlZ3JhdGVkQG1ldGEuZGF0YSksIHZhbHVlID0gVFJVRSksCiAgICAgICAgZnVuY3Rpb24oaSkgeyBsZW5ndGgodW5pcXVlKGtpZG5leS5pbnRlZ3JhdGVkQG1ldGEuZGF0YVssaV0pKSB9CiAgICAgICAgKQopICMgc2hvdyB0aGUgbnVtYmVyIG9mIGNsdXN0ZXJzIGF0IHRoZSB2YXJpb3VzIHJlc29sdXRpb25zCmBgYAoKYmFzZWQgb24gdGhlIHRhYnVsYSBtdXJpcyBzZW5pcyBhbm5vdGF0aW9uIHdlIGV4cGVjdCBhcm91bmQgMTggY2VsbCB0eXBlcyB0b3RhbC4KY2hvb3NlIHJlc29sdXRpb24gdGhhdCBnaXZlcyAxOSBjbHVzdGVycywgdGhleSBjYW4gYmUgbWVyZ2VkIGxhdGVyIG9uLgpgYGB7cn0KSWRlbnRzKGtpZG5leS5pbnRlZ3JhdGVkKSA8LSAiaW50ZWdyYXRlZF9zbm5fcmVzLjAuNCIgIyBhc3NpZ25pbmcgcmVzIHdpdGgxOSBjbHVzdGVycwpgYGAKCkRvIFVNQVAuIHVzaW5nIDIwIG5lYXJlc3QgbmVpZ2hib3JzIHRvIGdldCBiZXR0ZXIgbG9jYWwgc3RydWN0dXJlLCB3aGljaCBpcyBoZWxwZnVsIGZvciByZXNvbHZpbmcgc2ltaWxhciBjZWxsIHR5cGVzCmBgYHtyIHJldHVybiA9IEZBTFNFfQpraWRuZXkuaW50ZWdyYXRlZCAlPD4lIFJ1blVNQVAocmVkdWN0aW9uID0gInBjYSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkaW1zID0gUENzLCAgICAgICAjIHJ1bm5pbmcgVU1BUCBvbiBQQ3MgMS0yNwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbi5uZWlnaGJvcnMgPSAyMCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2ZXJib3NlID0gRkFMU0UpCmBgYAoKYGBge3IgZmlnLndpZHRoPTUuNSwgZmlnLmhlaWdodD0xMn0KcDEgPC0gRGltUGxvdChraWRuZXkuaW50ZWdyYXRlZCwgICNwbG90IDE6IGNvbG9yIHBvaW50cyBieSBzYW1wbGUKICAgICAgICAgICAgICByZWR1Y3Rpb24gPSAidW1hcCIsIAogICAgICAgICAgICAgIGdyb3VwLmJ5ID0gInNhbXBsZSIsIAogICAgICAgICAgICAgIHB0LnNpemUgPSAwLjIsIAogICAgICAgICAgICAgIHNodWZmbGUgPSBUKSArCiAgICAgICAgICAgIHNjYWxlX2NvbG9yX2JyZXdlcihwYWxldHRlID0gIkRhcmsyIikgKyAKICAgICAgICAgICAgICAgIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksIAogICAgICAgICAgICAgICAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemUgPSA5KSwKICAgICAgICAgICAgICAgICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplID0gOSksIAogICAgICAgICAgICAgICAgICAgICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDkpLAogICAgICAgICAgICAgICAgICAgICAgYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSA5KSkKCnAyIDwtIERpbVBsb3Qoa2lkbmV5LmludGVncmF0ZWQsICAgIyBwbG90IDI6IGNvbG9yIHBvaW50cyBieSBhZ2UKICAgICAgICAgICAgICByZWR1Y3Rpb24gPSAidW1hcCIsIAogICAgICAgICAgICAgIGdyb3VwLmJ5ID0gImFnZSIsIAogICAgICAgICAgICAgIHB0LnNpemUgPSAwLjIsIAogICAgICAgICAgICAgIHNodWZmbGUgPSBUKSArCiAgICAgICAgICAgICAgICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF9ibGFuaygpLCAKICAgICAgICAgICAgICAgICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChzaXplID0gOSksCiAgICAgICAgICAgICAgICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDkpLCAKICAgICAgICAgICAgICAgICAgICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSA5KSwKICAgICAgICAgICAgICAgICAgICAgIGF4aXMudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gOSkpCgpwMyA8LSBEaW1QbG90KGtpZG5leS5pbnRlZ3JhdGVkLCAgICMgcGxvdCAzOiBjb2xvciBwb2ludHMgYnkgbGVpZGVuIGNsdXN0ZXJzIChzdG9yZWQgYXMgJ2lkZW50JyBpbiBzZXVyYXQgb2JqZWN0KQogICAgICAgICAgICAgIHJlZHVjdGlvbiA9ICJ1bWFwIiwgCiAgICAgICAgICAgICAgZ3JvdXAuYnkgPSAnaWRlbnQnLAogICAgICAgICAgICAgIHB0LnNpemUgPSAwLjIsCiAgICAgICAgICAgICAgc2h1ZmZsZSA9IFRSVUUpICsgCiAgICAgICAgICAgICAgICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF9ibGFuaygpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDkpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplID0gOSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDkpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGF4aXMudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gOSkpIAoKcDEgJTw+JSBhZGp1c3QuYWxwaGEoYSA9IDAuNykgIyBhZGp1c3QgYWxwaGEgZm9yIGVhY2ggcGxvdApwMiAlPD4lIGFkanVzdC5hbHBoYShhID0gMC43KQpwMyAlPD4lIGFkanVzdC5hbHBoYShhID0gMC45KQoKcGxvdF9ncmlkKHAxLCBwMiwgcDMsIGxhYmVscyA9IGMoJ3NhbXBsZScsICdhZ2UnLCAnbGVpZGVuIGNsdXN0ZXJzJyksIG5jb2wgPSAxKQpgYGAKCnNvIHdlIGNhbiBzZWUgdGhhdCB0aGVyZSBpcyBjb25jb3JkYW50IGNsdXN0ZXJpbmcgYW1vbmcgYmF0Y2hlcyBpbnRvIHNoYXJlZCBjbHVzdGVycywgd2l0aCBzb21lIGRpZmZlcmVuY2VzIGJyZWFraW5nIGRvd24gYnkgY29uZGl0aW9uIChleHBlY3RlZCEpCgojIyBhbm5vdGF0ZSBjZWxsIHR5cGVzCgpJIHdhcyBob3BpbmcgdG8gYW5ub3RhdGUgYXV0b21hdGljYWxseSBieSB0cmFpbmluZyBhIG1vZGVsIG9uIHNvbWUgb2YgdGhlIG90aGVyIGFnZXMgb2YgVE1TIGRhdGEsIGJ1dCBJIHJhbiBpbnRvIHRyb3VibGUgY29udmVydGluZyB0aGUgQW5uZGF0YSBweXRob24gZm9ybWF0IGludG8gYSBmb3JtYXQgdGhhdCBpcyB1c2VhYmxlIGluIFI6CmBgYHtyfQojIGZpcnN0IGkgY3JlYXRlIGEgY29uZGEgZW52aXJvbm1lbnQgbG9jYWxseSB0aGVuOgojcmV0aWN1bGF0ZTo6dXNlX2NvbmRhZW52KCJhbm5kYXRhIikgIyBweXRob24gdmlhIHJldGljdWxhdGUKI2FkIDwtIHJldGljdWxhdGU6OmltcG9ydCgiYW5uZGF0YSIsIGNvbnZlcnQgPSBGQUxTRSkgIyBpbXBvcnRpbmcgYW5uZGF0YSBweXRob24gY2xhc3MKI2tpZG5leV9hZCA8LSBhZCRyZWFkX2g1YWQoInRhYnVsYS1tdXJpcy1zZW5pcy1kcm9wbGV0LXByb2Nlc3NlZC1vZmZpY2lhbC1hbm5vdGF0aW9ucy1LaWRuZXkuaDVhZCIpCiNraWRuZXkudG1zIDwtIENvbnZlcnQoa2lkbmV5X2FkLCB0byA9ICJzZXVyYXQiKQpgYGAKCnVuZm9ydHVuYXRlbHkgY2Fubm90IGdldCByZXRpY3VsYXRlIHRvIHdvcmsgYXMgZXhwZWN0ZWQgaGVyZS4KCkluc3RlYWQgSSBhbm5vdGF0ZWQgbWFudWFsbHkgYnkgY29tcGFyaW5nIG1hcmtlciBnZW5lcyBvZiBteSBjbHVzdGVycyB0byBleHByZXNzaW9uIGluIHRoZSBUTVMgY2VsbHhnZW5lIGJyb3dzZXIuCgpUaGUgZmlyc3Qgc3RlcCBpcyB0byBmaW5kIGNlbGwgdHlwZSBtYXJrZXJzIHRoYXQgZGVmaW5lIGNsdXN0ZXJzIChjb25zZXJ2ZWQgYWNyb3NzIGFnZXMgYW5kIHNhbXBsZXMpIHVzaW5nIHdpbGNveCByYW5rIHN1bSB0ZXN0cy4gdG8gZmluZCB0aGUgdG9wIG1hcmtlcnMsIEkgcmVxdWlyZWQgdGhhdCBtYXJrZXJzIGhhdmUgbG9nZm9sZGNoYW5nZSA+PSAwLjI1LCBhbmQgZGVmaW5lZCBieSBwcmVzZW5jZSAocG9zaXRpdmUgbWFya2VycykgcmF0aGVyIHRoYW4gYWJzZW5jZSAobmVnYXRpdmUgbWFya2VycykuIHRoaXMgcHJvY2VzcyBjb21wYXJlcyBleHByZXNzaW9uIGluIGVhY2ggY2x1c3RlciB0byBleHByZXNzaW9uIGFjcm9zcyBhbGwgb3RoZXIgY2x1c3RlcnMsIGkuZS4gY2x1c3RfaSB2cyAoYWxsIC0gY2x1c3RfaSk6CgpgYGB7cn0KIyB0aGUgZnVuY3Rpb24gYmVsb3cgcmVwbGFjZXMgdGhlIGluZGl2aWR1YWxseSB0cmFuc2Zvcm1lZCB2YWx1ZXMgZm9yIGVhY2ggYmF0Y2ggd2l0aCBhICMgc2luZ2xlIHNlcXVlbmNpbmcgZGVwdGggY292YXJpYXRlLgoKI2tpZG5leS5pbnRlZ3JhdGVkICU8PiUgUHJlcFNDVEZpbmRNYXJrZXJzKCkKCm1hcmtlcnNfYWxsIDwtIEZpbmRBbGxNYXJrZXJzKCAgICAgICMgY2x1c3RfaSB2cyAoYWxsIC0gY2x1c3RfaSkgd2lsY294IHJhbmsgc3VtIHRlc3QKICBvYmplY3QgPSBraWRuZXkuaW50ZWdyYXRlZCwKICBvbmx5LnBvcyA9IFRSVUUsICAKICBsb2dmYy50aHJlc2hvbGQgPSAwLjI1LCB2ZXJib3NlID0gRkFMU0UKKQoKZGltKG1hcmtlcnNfYWxsKVsxXSAjIGhvdyBtYW55IG1hcmtlcnM/CmBgYAoKZ2V0IHRvcCAxNSBtYXJrZXJzIGZvciBlYWNoIGNsdXN0ZXI6CmBgYHtyIHJldHVybj1GQUxTRX0KdG9wNSA8LSBsYXBwbHkodW5pcXVlKG1hcmtlcnNfYWxsJGNsdXN0ZXIpLAogICAgICAgICAgICAgICAgZnVuY3Rpb24oaSkgewogICAgICAgICAgICAgICAgaGVhZChzdWJzZXQobWFya2Vyc19hbGwsIGNsdXN0ZXIgPT0gaSksIG49NSkKICAgICAgICAgICAgICB9CiAgICAgICAgICAgICkKIyBoZXJlIGlzIGFuIGV4YW1wbGU6CnByaW50KHRvcDVbMV0pCmBgYAoKSSBjb21wYXJlZCBtYXJrZXIgZXhwcmVzc2lvbiBpbiB0aGlzIGRhdGFzZXQgd2l0aCB0aGUgVE1TIGluIGNlbGx4Z2VuZS4gaGVyZSBpcyBhbiBleGFtcGxlIG9mIHZpc3VhbGl6YXRpb24gb2YgYnJ1c2ggY2VsbCBtYXJrZXJzOgpgYGB7cn0KRGVmYXVsdEFzc2F5KGtpZG5leS5pbnRlZ3JhdGVkKSA8LSAnUk5BJyAjIHRoZSBSTkEgc2xvdCBob2xkcyB1bi10cmFuc2Zvcm1lZCB2YWx1ZXMgdGhhdCBiZXR0ZXIgY2FwdHVyZSBkaWZmZXJlbmNlcyBhbW9uZyBjZWxsIHR5cGVzCmtpZG5leS5pbnRlZ3JhdGVkICU+JSBWbG5QbG90KCdNZXAxYScpICMgZXhhbXBsZSBicnVzaCBjZWxsIG1hcmtlcgpgYGAKCm5vdyBhc3NpZ24gYWxsIG1hcmtlcnMuIEkgY29sbGFwc2VkIHNvbWUgY2x1c3RlcnMgdG9nZXRoZXIgYW5kIHdhc24ndCBhYmxlIHRvIGNhbGwgZXZlcnkKY2VsbCBpbiB0aGUgVE1TIGRhdGEuCgpgYGB7cn0Ka2lkbmV5LmludGVncmF0ZWQgJTw+JSBSZW5hbWVJZGVudHMoIjEiID0gImJydXNoX2NlbGwiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiMiIgPSAiYnJ1c2hfY2VsbCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIzIiA9ICJwcm94aW1hbF90dWJ1bGVfY2VsbCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICI0IiA9ICJmZW5lc3RyYXRlZF9jZWxsIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjUiID0gInByb3hpbWFsX3R1YnVsZV9jZWxsIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjYiID0gImRpc3RhbF9jb252b2x1dGVkX3R1YnVsZV9jZWxsIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjciID0gInRoaW5fTE9IIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjgiID0gInRoaWNrX0xPSCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICI5IiA9ICJtYWNyb3BoYWdlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjEwIiA9ICJwcm94aW1hbF90dWJ1bGVfY2VsbCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIxMSIgPSAiZmlicm9ibGFzdCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIxMiIgPSAiVF9jZWxsIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjEzIiA9ICJjb2xsZWN0aW5nX2R1Y3RfcHJpbmNpcGFsX2NlbGwiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiMTQiID0gIk5LX2NlbGwiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiMTUiID0gInBvZG9jeXRlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjE2IiA9ICJCX2NlbGwiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiMTciID0gImNhcGlsbGFyeV9lbmRvdGhlbGlhbF9jZWxsIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjE4IiA9ICJkaXN0YWxfY29udm9sdXRlZF90dWJ1bGVfY2VsbCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIxOSIgPSAibWVzYW5naWFsX2NlbGwiKQoKCmBgYAoKcGxvdCBVTUFQIGFnYWluIHdpdGggY2VsbCB0eXBlIGFubm90YXRpb246CgpgYGB7ciBmaWcud2lkdGg9OCwgZmlnLmhlaWdodD01fQpteWNvbHMgPC0gY29sb3JSYW1wUGFsZXR0ZShicmV3ZXIucGFsKDEyLCAiUGFpcmVkIikpKDE1KSAjIGV4cGFuZGluZyB0aGlzIHBhbGV0dGUgZnJvbSAxMiB0byAxNSBjb2xvcnMKCnBVTUFQIDwtIERpbVBsb3Qoa2lkbmV5LmludGVncmF0ZWQsIAogICAgICAgICAgICAgIHJlZHVjdGlvbiA9ICJ1bWFwIiwgCiAgICAgICAgICAgICAgcHQuc2l6ZSA9IDAuMiwKICAgICAgICAgICAgICBzaHVmZmxlID0gVFJVRSkgKyAKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gbXljb2xzKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemUgPSA5KSwKICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDkpLAogICAgICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSA5KSwKICAgICAgICBheGlzLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDkpKSAlPiUgYWRqdXN0LmFscGhhKGEgPSAwLjkpCgpwVU1BUApgYGAKCiMjIGNoYW5nZXMgaW4gY2VsbCB0eXBlIGNvbXBvc2l0aW9uIHdpdGggYWdlCgpwbG90IGNlbGwgdHlwZSByYXRpb3MgcGVyIHNhbXBsZToKYGBge3IgZmlnLndpZHRoPTgsIGZpZy5oZWlnaHQ9NX0KY2VsbHR5cGVYc2FtcGxlIDwtIGFzLmRhdGEuZnJhbWUodGFibGUoSWRlbnRzKGtpZG5leS5pbnRlZ3JhdGVkKSwgIyBnYXRoZXIgdXAgbnVtYmVyIG9mIGVhY2ggY2VsbCB0eXBlIHBlciBzYW1wbGUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAga2lkbmV5LmludGVncmF0ZWQkc2FtcGxlKSkgJT4lIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZXROYW1lcyhjKCJjZWxsX3R5cGUiLCAic2FtcGxlIiwgImNvdW50cyIpKQoKY2VsbHR5cGVYc2FtcGxlJHNhbXBsZSA8LSBmYWN0b3IoY2VsbHR5cGVYc2FtcGxlJHNhbXBsZSwgbGV2ZWxzID0gc2FtcGxlcykgIyBvcmRlciB0aGUgc2FtcGxlIG5hbWVzCgpjZWxsdHlwZV9hYnVuZF9wbG90IDwtIGdncGxvdChjZWxsdHlwZVhzYW1wbGUsIGFlcyhzYW1wbGUsIGNvdW50cywgZmlsbCA9IGNlbGxfdHlwZSkpICsgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGdlb21fYmFyKHBvc2l0aW9uPSJmaWxsIiwgc3RhdD0iaWRlbnRpdHkiKSArICMgcG9zaXRpb24gPSAnZmlsbCcgbm9ybWFsaXplcyBzY2FsZSBvZiBlYWNoIHNhbXBsZQogICAgICAgICAgICAgICAgICAgICAgICAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBteWNvbHMpICsKICAgICAgICAgICAgICAgICAgICAgICAgICAgdGhlbWVfbWluaW1hbChiYXNlX3NpemUgPSAgMTEpICsKICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFicyh5ID0gInJlbGF0aXZlIGFidW5kYW5jZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsbCA9ICJjZWxsIHR5cGUiKSArCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHRoZW1lKGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSkgCgpjZWxsdHlwZV9hYnVuZF9wbG90CmBgYAoKZm9yIGEgc3RhdGlzdGljYWwgdGVzdCBvZiBhYnVuZGFuY2VzIGFjcm9zcyBhZ2Ugd2UgbmVlZCBhIHR3by1zYW1wbGUgdGVzdCB0aGF0IGNhbiBhY2NvdW50IGZvciBzZXggYXMgYSByYW5kb20gdmFyaWFibGUuIHRoZSBwYWNrYWdlIHNwZWNrbGUvcHJvcGVsbGVyLiBkb2VzIGEgbW9kZXJhdGVkIHQtdGVzdCBvbiB0cmFuc2Zvcm1lZCBwcm9wb3J0aW9uYWwgZGF0YSBhbmQgaXMgdmVyeSBlYXN5IHRvIGFwcGx5IHRvIGEgc2V1cmF0IG9iamVjdC4gaHR0cHM6Ly93d3cuYmlvcnhpdi5vcmcvY29udGVudC8xMC4xMTAxLzIwMjEuMTEuMjguNDcwMjM2djEuZnVsbAoKYGBge3J9CmtpZG5leS5pbnRlZ3JhdGVkJGNlbGx0eXBlIDwtIElkZW50cyhraWRuZXkuaW50ZWdyYXRlZCkKCiMgdHJhbnNmb3JtZWQgY2VsbCB0eXBlIHByb3BvcnRpb25zOiAKcHJvcHMgPC0gc3BlY2tsZTo6Z2V0VHJhbnNmb3JtZWRQcm9wcyhraWRuZXkuaW50ZWdyYXRlZCRjZWxsdHlwZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBraWRuZXkuaW50ZWdyYXRlZCRzYW1wbGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHJhbnNmb3JtID0gJ2xvZ2l0JykKIyBtYWtlIGEgZGVzaWduIG1hdHJpeCB0aGF0IGFjY291bnRzIGZvciBhZ2UgYW5kIHNleDoKYWdlIDwtIGMocmVwKCcxOCcsIDMpLCByZXAoJzMnLCAzKSkKc2V4IDwtIGMoIk0iLCAiTSIsICJGIiwgIkYiLCAiRiIsICJNIikKZGF0YS5mcmFtZShhZ2UsIHNleCkKZGVzaWduIDwtIG1vZGVsLm1hdHJpeCh+IDAgKyBhZ2UgKyBzZXgpCm15Y29udHIgPC0gbWFrZUNvbnRyYXN0cyhhZ2UxOC1hZ2UzLCBsZXZlbHM9ZGVzaWduKQpteWNvbnRyICMgY29udHJhc3RzIGFnZSB3aXRoIHNleCBhcyBhIHJhbmRvbSBlZmZlY3QKYGBgCgppbXBsZW1lbnQgZW1waXJpY2FsIGJheWVzIG1vZGVyYXRlZCB0dGVzdC4gaGVyZSBhIGZyYWN0aW9uID4xIGluZGljYXRlcyBtb3JlIG9mIHRoYXQgY2VsbCB0eXBlIGluIGFnZSAxODoKYGBge3Igcm93cy5wcmludCA9IDE1fQpwcmludChwcm9wZWxsZXIudHRlc3QocHJvcC5saXN0ID0gcHJvcHMsCiAgICAgICAgICAgICAgICAgICAgICAgICBkZXNpZ24gPSBkZXNpZ24sCiAgICAgICAgICAgICAgICAgICAgICAgICByb2J1c3QgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgdHJlbmQgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgICAgIHNvcnQgPSBUUlVFLCAKICAgICAgICAgICAgICAgICAgICAgICAgIGNvbnRyYXN0cyA9IG15Y29udHIpKQpgYGAKCndlIGNhbiBzZWUgdGhhdCBuYXR1cmFsIGtpbGxlciBjZWxscyBhbmQgdGhpbiBsb29wIG9mIGhlbmxlIGFyZSBkaWZmZXJlbnRseSBhYnVuZGFudCB3aXRoIGEgc2lnbmlmaWNhbnQgcCB2YWx1ZSBidXQgdGhlIEZEUiBpcyAxMSUgYW5kIDM2JSByZXNwZWN0aXZlbHkuIHNvIG5vIGRpZmZlcmVuY2VzIHdlIGNhbiBjb25maWRlbnQgaW4gd2hlbiBhY2NvdW50aW5nIGZvciBtdWx0aXBsZSB0ZXN0aW5nLiBBZGRpdGlvbmFsbHkgd2UgY2FuIHNlZSB0aGF0IHRoZXJlIGFyZSBtYW55IG1vcmUgVCBhbmQgQiBjZWxscyBpbiAxOCBtb250aCBvbGQgbWljZSwgYnV0IHRoZXNlIGRpZmZlcmVuY2VzIGFyZSBub3Qgc2lnbmlmaWNhbnQgZWl0aGVyIC0gbGlrZWx5IGJlY2F1c2UgdGhlc2UgY2VsbHMgYXJlIHJhcmUgYW5kIGhhdmUgbGVzcyBwb3dlciB0byBkZXRlY3QgZGlmZmVyZW5jZXMuCgojIyBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbgogCnBlcmZvcm0gREUgYW5hbHlzaXMgb24gYWdlIGFtb25nIHRoZSB0d28gYmlnZ2VzdCBjbHVzdGVycy4gdGhlc2UgY2x1c3RlcnMgYXJlIHByb3hpbWFsIHR1YnVsZSBjZWxscyBhbmQgYnJ1c2ggY2VsbHM6CmBgYHtyfQp0YWJsZShJZGVudHMoa2lkbmV5LmludGVncmF0ZWQpKQpgYGAKCmNyZWF0ZSBhIG5ldyBkYXRhIGNsYXNzIHRoYXQgaGFzIGJvdGggY2VsbCB0eXBlIGFuZCBhZ2UgaW5mb3JtYXRpb246CmBgYHtyfQpraWRuZXkuaW50ZWdyYXRlZCRjZWxsdHlwZS5hZ2UgPC0gcGFzdGUoa2lkbmV5LmludGVncmF0ZWQkY2VsbHR5cGUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAga2lkbmV5LmludGVncmF0ZWQkYWdlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VwID0gIl8iKQpJZGVudHMoa2lkbmV5LmludGVncmF0ZWQpIDwtICJjZWxsdHlwZS5hZ2UiCmBgYAoKd2UgY2FuIGNvbnZlbmllbnRseSBpbXBsZW1lbnQgTUFTVCBmb3Igc2MgREUgZnJvbSBTZXVyYXQ6OkZpbmRNYXJrZXJzKCkKYGBge3J9CmtpZG5leS5QVEMuTUFTVCA8LSBraWRuZXkuaW50ZWdyYXRlZCAlPiUgCiAgICAgICAgICAgICAgICAgICAgICAgIEZpbmRNYXJrZXJzKGFzc2F5ID0gIlNDVCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzbG90ID0gImRhdGEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZGVudC4xID0gInByb3hpbWFsX3R1YnVsZV9jZWxsXzE4IiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlkZW50LjIgPSAicHJveGltYWxfdHVidWxlX2NlbGxfMyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRlc3QudXNlID0gIk1BU1QiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsb2dmYy50aHJlc2hvbGQgPSAwLjEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZlcmJvc2UgPSBGQUxTRSkgJT4lIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1YnNldChwX3ZhbF9hZGogPCAwLjA1KSAlPiUgIyBrZWVwIG9ubHkgc2lnbmlmaWNhbnRseSBERSBnZW5lcwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbXV0YXRlKGFicy5MRkMgPSBhYnMoYXZnX2xvZzJGQykpICMgY2FsY3VsYXRlIGFic29sdXRlIHZhbHVlIG9mIGV4cHJlc3Npb24gZGlmZmVyZW5jZQoKa2lkbmV5LkJDLk1BU1QgPC0ga2lkbmV5LmludGVncmF0ZWQgJT4lIAogICAgICAgICAgICAgICAgICAgICAgRmluZE1hcmtlcnMoYXNzYXkgPSAiU0NUIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzbG90ID0gImRhdGEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWRlbnQuMSA9ICJicnVzaF9jZWxsXzE4IiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZGVudC4yID0gImJydXNoX2NlbGxfMyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0ZXN0LnVzZSA9ICJNQVNUIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxvZ2ZjLnRocmVzaG9sZCA9IDAuMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZlcmJvc2UgPSBGQUxTRSkgJT4lIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdWJzZXQocF92YWxfYWRqIDwgMC4wNSkgJT4lIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbXV0YXRlKGFicy5MRkMgPSBhYnMoYXZnX2xvZzJGQykpCgojIGhvdyBtYW55IHNpZ25pZmljYW50IERFIGdlbmVzPwpjYXQocGFzdGUwKGRpbShraWRuZXkuUFRDLk1BU1QpWzFdLCAiIERFIGdlbmVzIGluIFBUQ1xuIiwgCiAgICAgICAgICAgICBkaW0oa2lkbmV5LkJDLk1BU1QpWzFdLCAiIERFIGdlbmVzIGluIEJDIikpCmBgYAoKb2JzZXJ2ZSB0aGUgdG9wIDE1IERFIGdlbmVzIChvcmRlcmVkIGJ5IGFic29sdXRlIGxvZzJGQykgZm9yIGVhY2ggY2VsbCB0eXBlLgpBIHBvc2l0aXZlIGxvZzJGQyBpbmRpY2F0ZXMgZ3JlYXRlciBleHByZXNzaW9uIGluIGFnZSAxOG1vIHZzIDNtbwpgYGB7ciByb3dzLnByaW50ID0gMTV9CnRvcDE1LlBUQyA8LSBoZWFkKGtpZG5leS5QVEMuTUFTVFtvcmRlcihraWRuZXkuUFRDLk1BU1QkYWJzLkxGQywgZGVjcmVhc2luZyA9IFRSVUUpLCBdLCBuID0gMTUpCnRvcDE1LkJDIDwtIGhlYWQoa2lkbmV5LkJDLk1BU1Rbb3JkZXIoa2lkbmV5LkJDLk1BU1QkYWJzLkxGQywgZGVjcmVhc2luZyA9IFRSVUUpLCBdLCBuID0gMTUpCmBgYAoKdG9wIDE1IERFIGluIHByb3hpbWFsIHR1YnVsZSBjZWxsczoKYGBge3J9CnRvcDE1LlBUQwpgYGAKCnRvcCAxNSBERSBpbiBicnVzaCBjZWxsczoKYGBge3J9CnRvcDE1LkJDCmBgYAoKaG93IG11Y2ggb3ZlcmxhcCBpcyB0aGVyZSBiZXR3ZWVuIHRoZSBzaWduaWZpY2FudGx5IERFIGdlbmVzIGluIHRoZXNlIGNlbGxzCmBgYHtyfQpzaGFyZWQuREUgPC0gcm93bmFtZXMoa2lkbmV5LkJDLk1BU1QpW3Jvd25hbWVzKGtpZG5leS5CQy5NQVNUKSAlaW4lIHJvd25hbWVzKGtpZG5leS5QVEMuTUFTVCldCmxlbmd0aChzaGFyZWQuREUpCmBgYAoKOTMgZ2VuZXMgaW4gY29tbW9uLiBtYWtlcyBzZW5zZSBhcyB0aGVzZSBhcmUgdmVyeSBzaW1pbGFyIGNlbGwgdHlwZXMhCgp2aXN1YWxpemUgYSBmZXcgb2YgdGhlIHRvcCBERSBnZW5lczoKCmBgYHtyfQojIHdlIG5lZWQgdG8gbm9ybWFsaXplIHRoZSBjb3VudCBkYXRhIGluIHRoZSBSTkEgc2xvdCBmb3IgREUgdmlzdWFsaXphdGlvbgpraWRuZXkuaW50ZWdyYXRlZCAlPD4lIE5vcm1hbGl6ZURhdGEoYXNzYXkgPSAiUk5BIikKYGBgCgpgYGB7ciBmaWcud2lkdGg9OC41LCBmaWcuaGVpZ2h0PTE2fQpQVEMuREUudmxuIDwtIFZsblBsb3Qoa2lkbmV5LmludGVncmF0ZWQsCiAgICAgICAgICAgICAgICAgICAgICBhc3NheSA9ICJSTkEiLAogICAgICAgICAgICAgICAgICAgICAgc2xvdCA9ICJkYXRhIiwKICAgICAgICAgICAgICAgICAgICAgIGZlYXR1cmVzPSBoZWFkKHJvd25hbWVzKHRvcDE1LlBUQykpLAogICAgICAgICAgICAgICAgICAgICAgaWRlbnRzID0gYygicHJveGltYWxfdHVidWxlX2NlbGxfMyIsICJwcm94aW1hbF90dWJ1bGVfY2VsbF8xOCIpKSAmCiAgICAgICAgICAgICAgICAgICAgICAgIHRoZW1lKGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSkKQkMuREUudmxuIDwtIFZsblBsb3Qoa2lkbmV5LmludGVncmF0ZWQsCiAgICAgICAgICAgICAgICAgICAgIGFzc2F5ID0gIlJOQSIsCiAgICAgICAgICAgICAgICAgICAgIHNsb3QgPSAiZGF0YSIsCiAgICAgICAgICAgICAgICAgICAgIGZlYXR1cmVzPSBoZWFkKHJvd25hbWVzKHRvcDE1LkJDKSksCiAgICAgICAgICAgICAgICAgICAgIGlkZW50cyA9IGMoImJydXNoX2NlbGxfMyIsICJicnVzaF9jZWxsXzE4IikpICYKICAgICAgICAgICAgICAgICAgICAgICAgdGhlbWUoYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpKQoKcGxvdF9ncmlkKFBUQy5ERS52bG4sIEJDLkRFLnZsbiwgbmNvbCA9IDEpCmBgYAoKCiMjIHBhdGh3YXkgYW5hbHlzaXMgb2YgREUgZ2VuZXMKCndlIGNhbiB1c2UgcGFja2FnZSBjbHVzdGVyUHJvZmlsZXIgdG8gZG8gcGF0aHdheSBlbnJpY2htZW50IGFnYWluc3Qgb25saW5lIEtFR0cgZGF0YWJhc2UKYGBge3J9CnNlYXJjaF9rZWdnX29yZ2FuaXNtKCdtbXUnLCBieT0na2VnZ19jb2RlJykKYGBgCgpwcmVwYXJlIGdlbmUgbGlzdHMgZm9yIGVucmljaG1lbnQgYW5hbHlzaXMuIHdlIG5lZWQgdG8gY29udmVydCBnZW5lIHN5bWJvbHMgdG8gZW50cmV6IElEcyB0byBzZWFyY2ggS0VHRwpgYGB7cn0KIyBERSBnZW5lIGxpc3RzClBUQy5nZW5lbGlzdCA8LSByb3duYW1lcyhraWRuZXkuUFRDLk1BU1QpCkJDLmdlbmVsaXN0IDwtIHJvd25hbWVzKGtpZG5leS5CQy5NQVNUKQoKIyBiYWNrZ3JvdW5kIGdlbmUgbGlzdHMgKGFsbCBnZW5lcyBleHByZXNzZWQgaW4gZm9jYWwgY2VsbCB0eXBlKQpJZGVudHMoa2lkbmV5LmludGVncmF0ZWQpIDwtICJjZWxsdHlwZSIKClBUQy50b3RhbGNvdW50cyA8LSByb3dTdW1zKHN1YnNldChraWRuZXkuaW50ZWdyYXRlZCwgaWRlbnRzID0gInByb3hpbWFsX3R1YnVsZV9jZWxsIilAYXNzYXlzJFJOQUBjb3VudHMpClBUQy5iYWNrZ3JvdW5kIDwtIG5hbWVzKFBUQy50b3RhbGNvdW50c1tQVEMudG90YWxjb3VudHMgPiAxMDBdKSAjIGRlZmluZSAnZXhwcmVzc2VkJyBhcyA+IDEwMCB0b3RhbCBjb3VudHMgcGVyIGdlbmUKQkMudG90YWxjb3VudHMgPC0gcm93U3VtcyhzdWJzZXQoa2lkbmV5LmludGVncmF0ZWQsIGlkZW50cyA9ICJicnVzaF9jZWxsIilAYXNzYXlzJFJOQUBjb3VudHMpCkJDLmJhY2tncm91bmQgPC0gbmFtZXMoQkMudG90YWxjb3VudHNbQkMudG90YWxjb3VudHMgPiAxMDBdKSAjIGRlZmluZSAnZXhwcmVzc2VkJyBhcyA+IDEwMCB0b3RhbCBjb3VudHMgcGVyIGdlbmUKCiMgZmlyc3QgY29udmVydCB0byBlbnNlbWJsIElEcyB1c2luZyB0aGUgZ2VuZXMudHN2IGRhdGEgc3VwcGxpZWQgZm9yIHRoaXMgYXNzaWdubWVudC4gCiMgd2UgZ2V0IGEgZmV3IG1vcmUgbWF0Y2hlcyB0byBlbnRyZXogZ29pbmcgZnJvbSBlbnNlbWJsIHJhdGhlciB0aGFuIGdlbmUgc3ltYm9scy4KbW91c2VfZ2VuZXMgPC0gcmVhZC50YWJsZSgiMy1NLTgvZ2VuZXMudHN2IikgCgojIG1hdGNoIHN5bWJvbHMgaW4gZ2VuZSBsaXN0cyB0byB0aGUgc3ltYm9sK2Vuc2VtYmwgdGFibGUKUFRDLmdlbmVsaXN0IDwtIGxlZnRfam9pbihkYXRhLmZyYW1lKFBUQy5nZW5lbGlzdCksIG1vdXNlX2dlbmVzLCBieSA9IGMoIlBUQy5nZW5lbGlzdCIgPSAiVjIiKSkKQkMuZ2VuZWxpc3QgPC0gbGVmdF9qb2luKGRhdGEuZnJhbWUoQkMuZ2VuZWxpc3QpLCBtb3VzZV9nZW5lcywgYnkgPSBjKCJCQy5nZW5lbGlzdCIgPSAiVjIiKSkKUFRDLmJhY2tncm91bmQgPC0gbGVmdF9qb2luKGRhdGEuZnJhbWUoUFRDLmJhY2tncm91bmQpLCBtb3VzZV9nZW5lcywgYnkgPSBjKCJQVEMuYmFja2dyb3VuZCIgPSAiVjIiKSkKQkMuYmFja2dyb3VuZCA8LSBsZWZ0X2pvaW4oZGF0YS5mcmFtZShCQy5iYWNrZ3JvdW5kKSwgbW91c2VfZ2VuZXMsIGJ5ID0gYygiQkMuYmFja2dyb3VuZCIgPSAiVjIiKSkKCiMgbm93IGdldCBlbnRyZXogSURzIGZyb20gZW5zZW1ibCBJRHMKSURzIDwtIGxhcHBseShsaXN0KFBUQy5nZW5lbGlzdCwgCiAgICAgICAgICAgICAgICAgICAgQkMuZ2VuZWxpc3QsIAogICAgICAgICAgICAgICAgICAgIFBUQy5iYWNrZ3JvdW5kLCAKICAgICAgICAgICAgICAgICAgICBCQy5iYWNrZ3JvdW5kKSwgCiAgICAgICAgICAgICAgIGZ1bmN0aW9uKGkpIHtzZWxlY3Qob3JnLk1tLmVnLmRiLCAgICMgdGhpcyBzZWFyY2hlcyB0aGUgbmNiaSBtb3VzZSBnZW5lIGRhdGFiYXNlIGFuZCByZXR1cm5zIGdlbmUgYWxpYXNlcwogICAgICAgICAgICAgICAgICAgICAgICAgICAga2V5cyA9IHVubGlzdChsYXBwbHkoc3RyX3NwbGl0KGkkVjEsICdcXC4nKSwgZnVuY3Rpb24oeCkgeFsxXSkpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sdW1ucyA9IGMoIkVOVFJFWklEIiwgIkVOU0VNQkwiLCAiU1lNQk9MIiksICMgcmV0dXJuIGVudHJleiwgZW5zZW1ibCwgYW5kIGdlbmUgc3ltYm9sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBrZXl0eXBlID0gIkVOU0VNQkwiKSAjIHNlYXJjaGluZyB3aXRoIGVuc2VtYmwgSURzCiAgICAgICAgICAgICAgIH0KKQoKIyBhbmQgbm93IGxpc3QgdGhlIGVudHJleiBpZHMgdGhhdCB3ZXJlIGZvdW5kIChleGNsdWRpbmcgdGhlIE5BcyB0aGF0IGNvdWxkbnQgYmUgZm91bmQgaW4gdGhlIGRhdGFiYXNlKQpQVEMuZ2VuZWxpc3QuZW50cmV6IDwtIGRhdGEuZnJhbWUoSURzWzFdKSRFTlRSRVpJRFshaXMubmEoZGF0YS5mcmFtZShJRHNbMV0pJEVOVFJFWklEKV0KQkMuZ2VuZWxpc3QuZW50cmV6IDwtIGRhdGEuZnJhbWUoSURzWzJdKSRFTlRSRVpJRFshaXMubmEoZGF0YS5mcmFtZShJRHNbMl0pJEVOVFJFWklEKV0KUFRDLmJhY2tncm91bmQuZW50cmV6IDwtIGRhdGEuZnJhbWUoSURzWzNdKSRFTlRSRVpJRFshaXMubmEoZGF0YS5mcmFtZShJRHNbM10pJEVOVFJFWklEKV0KQkMuYmFja2dyb3VuZC5lbnRyZXogPC0gZGF0YS5mcmFtZShJRHNbNF0pJEVOVFJFWklEWyFpcy5uYShkYXRhLmZyYW1lKElEc1s0XSkkRU5UUkVaSUQpXQpgYGAKCm5vdyBzZWFyY2ggdGhlIEtFR0cgZGF0YWJhc2UgZm9yIHNpZ25pZmljYW50IHBhdGh3YXkgZW5yaWNobWVudCBpbiBwcm94aW1hbCB0dWJ1bGUgY2VsbCBERSBnZW5lczoKYGBge3J9ClBUQy5rZWdnIDwtIGVucmljaEtFR0coZ2VuZSA9IFBUQy5nZW5lbGlzdC5lbnRyZXosCiAgICAgICAgICAgICAgICAgICAgICAgdW5pdmVyc2UgPSBQVEMuYmFja2dyb3VuZC5lbnRyZXosCiAgICAgICAgICAgICAgICAgICAgICAgb3JnYW5pc20gID0gJ21tdScsCiAgICAgICAgICAgICAgICAgICAgICAgcHZhbHVlQ3V0b2ZmID0gMC4wNSkKYGBgCgphbmQgYnJ1c2ggY2VsbCBERSBnZW5lczoKYGBge3J9CkJDLmtlZ2cgPC0gZW5yaWNoS0VHRyhnZW5lID0gQkMuZ2VuZWxpc3QuZW50cmV6LAogICAgICAgICAgICAgICAgICAgICAgIHVuaXZlcnNlID0gQkMuYmFja2dyb3VuZC5lbnRyZXosCiAgICAgICAgICAgICAgICAgICAgICAgb3JnYW5pc20gID0gJ21tdScsCiAgICAgICAgICAgICAgICAgICAgICAgcHZhbHVlQ3V0b2ZmID0gMC4wNSkKYGBgCgpyZXN1bHRzIGZvciBwcm94aW1hbCB0dWJ1bGUgY2VsbCBwYXRod2F5czoKYGBge3IgY29scy5wcmludCA9IDEwfQpwcmludC5kYXRhLmZyYW1lKGhlYWQoUFRDLmtlZ2cpKQpgYGAKCmFuZCBicnVzaCBjZWxsczoKYGBge3J9CnByaW50LmRhdGEuZnJhbWUoaGVhZChCQy5rZWdnKSkKYGBgCgo=